diff options
459 files changed, 21707 insertions, 33055 deletions
diff --git a/.gitignore b/.gitignore index 3a5fef871..244c938a5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *~ +*.pem *.swp /.config.cache @@ -11,8 +12,7 @@ /run /bin -/include/inspircd_config.h -/include/inspircd_version.h +/include/config.h /src/modules/m_geoip.cpp /src/modules/m_ldapauth.cpp @@ -22,6 +22,7 @@ /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 @@ -1,3 +1,10 @@ +### 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, @@ -33,35 +33,17 @@ BEGIN { 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 (); @@ -74,36 +56,23 @@ my @opt_disableextras; 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) { @@ -125,30 +94,20 @@ our $interactive = !( (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 $topdir = getcwd(); +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} = $topdir."/run"; if (defined $opt_base_dir) { $config{BASE_DIR} = $opt_base_dir; @@ -157,21 +116,21 @@ if (defined $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{BUILD_DIR} = $topdir."/build"; + $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{BUILD_DIR} = rel2abs($topdir."/build"); + $config{DATA_DIR} = rel2abs($config{BASE_DIR}."/data"); + $config{LOG_DIR} = rel2abs($config{BASE_DIR}."/logs"); } if (defined $opt_config_dir) { @@ -189,89 +148,37 @@ if (defined $opt_data_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} = ""; - } -} +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{STARTSCRIPT} = $^O eq 'darwin' ? 'org.inspircd.plist' : 'inspircd'; -$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{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{USE_PORTS} = "y"; # epoll enabled -if (defined $opt_noports) -{ - $config{USE_PORTS} = "n"; -} -$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. -} -else -{ - $config{CC} = "g++"; # C++ compiler -} -if (defined $opt_cc) -{ - $config{CC} = $opt_cc; + +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; } -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; @@ -279,156 +186,46 @@ if ($config{HAS_OPENSSL} =~ /^([-[:digit:].]+)(?:[a-z])?(?:\-[a-z][0-9])?/) { $config{HAS_OPENSSL} = ""; } -if (($config{GCCVER} eq "") || ($config{GCCMINOR} eq "")) { - if ($config{IS_DARWIN} eq "YES") { - print $config{CC} . " was not found! You require clang++ (the LLVM C++ compiler, part of the OSX developer tools) to build InspIRCd!\n"; - } else { - print $config{CC} . " was not found! You require g++ (the GNU C++ compiler, part of GCC) to build InspIRCd!\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"; - -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; + +# Select is available on all platforms +$config{HAS_SELECT} = 1; +$config{SOCKETENGINE} ||= "select"; + +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"; @@ -442,7 +239,7 @@ if (defined($config{HAS_GNUTLS}) && (($config{HAS_GNUTLS}) || ($config{HAS_GNUTL $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"; @@ -456,38 +253,14 @@ if (defined($config{HAS_OPENSSL}) && (($config{HAS_OPENSSL}) || ($config{HAS_OPE $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 $revision = get_revision(); + chomp(my $version = `sh src/version.sh`); - my $rev = getrevision(); # Display Introduction Message.. print <<"STOP" ; Welcome to the \e[1mInspIRCd\e[0m configuration program! (\e[1minteractive mode\e[0m) @@ -501,143 +274,108 @@ Press \e[1m<RETURN>\e[0m to accept the default for any option, or enter 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"; - - $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"; - - 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"; - } - } - } + print "Your InspIRCd version is: \e[1;32m"; + print $revision eq 'release' ? substr($version, 9) : substr($revision, 1); + 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"; + + # Check that the user actually wants this version. + if (index($version, '+') != -1) { + 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. + +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}); + $config{BUILD_DIR} = prompt_dir(1, 'In what directory do you want the build to take place?', $config{BUILD_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"; } } } @@ -647,375 +385,121 @@ should NOT be used. You should probably specify a newer compiler.\n\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); +writefiles(); +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); +print "*** \e[1;32mRemember to edit your configuration files!!!\e[0m ***\n\n"; 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 $revision = get_revision(); 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; + print "Writing \e[1;32mconfig.h\e[0m\n"; + open(FILEHANDLE, ">include/config.h.tmp"); + print FILEHANDLE <<EOF; /* Auto generated by configure, do not modify! */ -#ifndef __CONFIGURATION_AUTO__ -#define __CONFIGURATION_AUTO__ +#pragma once -/* this is for windows support. */ -#define CoreExport /**/ -#define DllExport /**/ +#define BRANCH "$branch" +#define VERSION "$version" +#define REVISION "$revision" +#define SYSTEM "$incos" +#define INSPIRCD_SOCKETENGINE_NAME "$config{SOCKETENGINE}" #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"; - } - } + if ($config{HAS_EVENTFD}) { + print FILEHANDLE "#define HAS_EVENTFD\n"; } + if ($config{HAS_CLOCK_GETTIME}) { + print FILEHANDLE "#define HAS_CLOCK_GETTIME\n"; + } + + print FILEHANDLE "\n#include \"threadengines/threadengine_pthread.h\"\n"; + close(FILEHANDLE); - # Write all .in files. - my $tmp = ""; - my $file = ""; - my $exe = "inspircd"; + unlink 'include/config.h'; + rename 'include/config.h.tmp', 'include/config.h'; # 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'; + push @dotfiles, 'org.inspircd.plist' if $^O 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"); + $config{BUILD_DIR} ||= rel2abs($topdir."/build"); + $config{COMPILER} = lc $cxx{NAME}; + $config{SYSTEM} = lc $^O; for my $var (qw( - CC SYSTEM BASE_DIR CONFIG_DIR MODULE_DIR BINARY_DIR BUILD_DIR DATA_DIR UID - STARTSCRIPT DESTINATION SOCKETENGINE + CXX COMPILER SYSTEM BASE_DIR CONFIG_DIR MODULE_DIR BINARY_DIR BUILD_DIR DATA_DIR UID + STARTSCRIPT SOCKETENGINE )) { s/\@$var\@/$config{$var}/g; } - s/\@EXECUTABLE\@/$exe/ if defined $exe; + s/\@VERSION\@/$version/ if defined $version; if ($file eq 'main.mk') { @@ -1025,6 +509,7 @@ EOF s/\@IFDEF (\S+)/ifdef $1/g; s/\@IFNDEF (\S+)/ifndef $1/g; s/\@IFEQ (\S+) (\S+)/ifeq ($1,$2)/g; + s/\@IFNEQ (\S+) (\S+)/ifneq ($1,$2)/g; s/\@ELSIFEQ (\S+) (\S+)/else ifeq ($1,$2)/g; s/\@ELSE/else/g; s/\@ENDIF/endif/g; @@ -1040,6 +525,7 @@ EOF s/\@IFDEF (\S+)/.if defined($1)/g; s/\@IFNDEF (\S+)/.if !defined($1)/g; s/\@IFEQ (\S+) (\S+)/.if $1 == $2/g; + s/\@IFNEQ (\S+) (\S+)/.if $1 != $2/g; s/\@ELSIFEQ (\S+) (\S+)/.elif $1 == $2/g; s/\@ELSE/.else/g; s/\@ENDIF/.endif/g; @@ -1058,34 +544,7 @@ EOF } } - 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; - } + chmod 0750, 'inspircd'; } # Routine to list out the extra/ modules that have been enabled. @@ -1151,7 +610,7 @@ EXTRA: for my $extra (@extras) { 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. @@ -1198,10 +657,10 @@ sub enable_extras (@) { 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; @@ -1234,7 +693,7 @@ EXTRA: for my $extra (@extras) { } # 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. diff --git a/docs/conf/helpop-full.conf.example b/docs/conf/helpop-full.conf.example index 556d4c1a8..7b819845f 100644 --- a/docs/conf/helpop-full.conf.example +++ b/docs/conf/helpop-full.conf.example @@ -34,7 +34,7 @@ UNINVITE AWAY DCCALLOW SILENCE ACCEPT 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 @@ -97,6 +97,11 @@ 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 @@ -260,11 +265,6 @@ Show the message of the day for [server]. Messages of the day often 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. @@ -374,7 +374,7 @@ SAJOIN SAPART SAMODE SATOPIC SAKICK KILL SAQUIT GLINE ZLINE QLINE KLINE RLINE ELINE CBAN SHUN -FILTER OJOIN +FILTER OJOIN CLEARCHAN CONNECT SQUIT RCONNECT RSQUIT @@ -756,6 +756,16 @@ Reloads the specified core command."> 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 # ###################### @@ -803,15 +813,15 @@ Closes all unregistered connections to the local server."> 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 @@ -868,6 +878,9 @@ Closes all unregistered connections to the local server."> 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 diff --git a/docs/conf/helpop.conf.example b/docs/conf/helpop.conf.example index 555409863..7d505b261 100644 --- a/docs/conf/helpop.conf.example +++ b/docs/conf/helpop.conf.example @@ -37,7 +37,7 @@ UNINVITE AWAY DCCALLOW SILENCE ACCEPT 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 @@ -61,7 +61,7 @@ SAJOIN SAPART SAMODE SATOPIC SAKICK KILL SAQUIT GLINE ZLINE QLINE KLINE RLINE ELINE CBAN SHUN -FILTER +FILTER CLEARCHAN CONNECT SQUIT RCONNECT RSQUIT @@ -114,15 +114,15 @@ LOCKSERV UNLOCKSERV JUMPSERVER"> 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 @@ -178,6 +178,9 @@ LOCKSERV UNLOCKSERV JUMPSERVER"> 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 diff --git a/docs/conf/inspircd.conf.example b/docs/conf/inspircd.conf.example index 2617d1e6c..fd625aef1 100644 --- a/docs/conf/inspircd.conf.example +++ b/docs/conf/inspircd.conf.example @@ -34,6 +34,15 @@ # # ######################################################################## +#-#-#-#-#-#-#-#-#-# 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 # @@ -65,11 +74,6 @@ # # # 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"> @@ -153,6 +157,17 @@ # for ssl to work. If you do not want this bind section 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"> @@ -265,6 +280,10 @@ # 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" @@ -372,6 +391,10 @@ # 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" @@ -412,11 +435,11 @@ # 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 -#-#-#-#-#-#-#-#-#-# # # @@ -424,13 +447,12 @@ # 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"> +#<execfiles motd="wget -O - http://www.example.com/motd.txt"> #-#-#-#-#-#-#-#-#-#-#-# MAXIMUM CHANNELS -#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # # @@ -551,11 +573,6 @@ # 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. @@ -594,11 +611,11 @@ # 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" @@ -609,12 +626,7 @@ # 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 #-#-#-#-#-#-#-#-#-#-# @@ -629,33 +641,37 @@ # 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: @@ -704,8 +720,8 @@ # (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: @@ -769,6 +785,9 @@ # 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" @@ -784,6 +803,14 @@ # 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 @@ -798,7 +825,7 @@ # 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. # @@ -835,7 +862,7 @@ # 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 -#-#-#-#-#-#-#-#-#-#-#-#-# # # @@ -953,7 +980,7 @@ # 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 @@ -965,10 +992,10 @@ # 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"> ######################################################################### diff --git a/docs/conf/links.conf.example b/docs/conf/links.conf.example index e4ebea556..dbb29f1ff 100644 --- a/docs/conf/links.conf.example +++ b/docs/conf/links.conf.example @@ -29,7 +29,7 @@ # 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 @@ -95,7 +95,7 @@ # 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 diff --git a/docs/conf/modules.conf.example b/docs/conf/modules.conf.example index b4a5afab6..b17fd73fb 100644 --- a/docs/conf/modules.conf.example +++ b/docs/conf/modules.conf.example @@ -313,11 +313,12 @@ # 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 #-#-#-#-#-#-#-#-#-#-#-#-# @@ -381,7 +382,8 @@ # 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 @@ -415,32 +417,6 @@ #<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, # IP addresses and hosts. @@ -479,6 +455,11 @@ #<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. # To use, you should enable m_conn_umodes and add +x as @@ -491,7 +472,7 @@ # 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 # @@ -500,19 +481,9 @@ # 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" @@ -540,7 +511,9 @@ #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # 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 -#-#-#-#-#-#-#-#-#-#-# @@ -548,7 +521,10 @@ # 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> @@ -594,7 +570,9 @@ # # 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. @@ -618,7 +596,6 @@ #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # 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. @@ -767,7 +744,15 @@ # 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 @@ -812,18 +797,15 @@ #<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- @@ -855,6 +837,11 @@ #<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"> @@ -952,8 +939,6 @@ #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # 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. @@ -969,24 +954,38 @@ #<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"> # # # @@ -1000,28 +999,17 @@ # 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. # @@ -1041,20 +1029,13 @@ #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # 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 # @@ -1093,6 +1074,11 @@ #<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. # This module is in extras. Re-run configure with: @@ -1264,7 +1250,7 @@ # 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. @@ -1349,8 +1335,8 @@ # # 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. @@ -1417,6 +1403,12 @@ #<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 # system (i.e.: any Linux, BSD, but not Windows). You must have at @@ -1461,6 +1453,26 @@ #<module name="m_remove.so"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# +# 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. # # You probably *DO NOT* want to load this module on a public network. @@ -1500,10 +1512,19 @@ # 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"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# @@ -1630,14 +1651,50 @@ #<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. # This module is oper-only by default. #<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, @@ -1768,10 +1825,17 @@ #<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"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# diff --git a/docs/conf/modules/unrealircd.conf.example b/docs/conf/modules/unrealircd.conf.example index 9518822de..2bda9d840 100644 --- a/docs/conf/modules/unrealircd.conf.example +++ b/docs/conf/modules/unrealircd.conf.example @@ -93,15 +93,6 @@ <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"> diff --git a/docs/conf/opers.conf.example b/docs/conf/opers.conf.example index d03d019c6..524ebce34 100644 --- a/docs/conf/opers.conf.example +++ b/docs/conf/opers.conf.example @@ -24,6 +24,7 @@ # - 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 $*) + # - users/samode-usermodes: allows opers with this priv to change the user modes of any other user using /SAMODE # - channels/high-join-limit: allows opers with this priv to join <channels:opers> total channels instead of <channels:users> total channels. # PERMISSIONS: # - users/flood/no-fakelag: prevents opers from being penalized with fake lag for flooding (*NOTE) @@ -55,8 +56,6 @@ <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. diff --git a/docs/conf/rules.txt.example b/docs/conf/rules.txt.example deleted file mode 100644 index e51f0afd9..000000000 --- a/docs/conf/rules.txt.example +++ /dev/null @@ -1,3 +0,0 @@ -This is the InspIRCd rules file. - -Place any network or server rules here :) diff --git a/docs/rfc/rfc1035.txt b/docs/rfc/rfc1035.txt deleted file mode 100644 index b1a9bf5a9..000000000 --- a/docs/rfc/rfc1035.txt +++ /dev/null @@ -1,3077 +0,0 @@ -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - diff --git a/docs/rfc/rfc1413.txt b/docs/rfc/rfc1413.txt deleted file mode 100644 index 17ede58a2..000000000 --- a/docs/rfc/rfc1413.txt +++ /dev/null @@ -1,451 +0,0 @@ - - - - - - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] -
\ No newline at end of file diff --git a/docs/rfc/rfc1459.txt b/docs/rfc/rfc1459.txt deleted file mode 100644 index 09fbf34f7..000000000 --- a/docs/rfc/rfc1459.txt +++ /dev/null @@ -1,3643 +0,0 @@ - - - - - - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] - -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] -
\ No newline at end of file diff --git a/include/bancache.h b/include/bancache.h index a7aac7f17..7f51ca75e 100644 --- a/include/bancache.h +++ b/include/bancache.h @@ -18,8 +18,7 @@ */ -#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 @@ -37,68 +36,50 @@ class CoreExport BanCacheHit /** 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) + BanCacheHit(const std::string &type, const std::string &reason, time_t seconds) + : Type(type), Reason(reason), Expiry(ServerInstance->Time() + seconds) { - 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. } - // 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; +typedef TR1NS::unordered_map<std::string, BanCacheHit*, TR1NS::hash<std::string> > BanCacheHash; /** A manager for ban cache, which allocates and deallocates and checks cached bans. */ class CoreExport BanCacheManager { - private: 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 diff --git a/include/base.h b/include/base.h index 0a4456f3a..dcbb2e5c7 100644 --- a/include/base.h +++ b/include/base.h @@ -20,8 +20,7 @@ */ -#ifndef BASE_H -#define BASE_H +#pragma once #include <map> #include <deque> @@ -180,21 +179,23 @@ class reference */ 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. @@ -203,17 +204,14 @@ class CoreExport CoreException : public std::exception */ 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; @@ -250,10 +248,10 @@ class CoreExport ServiceProvider : public classbase 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(); +}; diff --git a/include/builtinmodes.h b/include/builtinmodes.h new file mode 100644 index 000000000..e78e68b11 --- /dev/null +++ b/include/builtinmodes.h @@ -0,0 +1,178 @@ +/* + * 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 +i + */ +class ModeChannelInviteOnly : public SimpleChannelModeHandler +{ + public: + ModeChannelInviteOnly() : SimpleChannelModeHandler(NULL, "inviteonly", 'i') + { + } +}; + +/** 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 +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 +o + */ +class ModeChannelOp : public PrefixMode +{ + public: + ModeChannelOp(); +}; + +/** 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') + { + } +}; + +/** Channel mode +v + */ +class ModeChannelVoice : public PrefixMode +{ + public: + ModeChannelVoice(); +}; + +/** User mode +i + */ +class ModeUserInvisible : public SimpleUserModeHandler +{ + public: + ModeUserInvisible() : SimpleUserModeHandler(NULL, "invisible", 'i') + { + } +}; + +/** 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); +}; diff --git a/include/caller.h b/include/caller.h index 64b37611f..c3a29e8c2 100644 --- a/include/caller.h +++ b/include/caller.h @@ -3,6 +3,7 @@ * * 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 @@ -18,8 +19,79 @@ */ -#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 diff --git a/include/channels.h b/include/channels.h index dda53f69d..628f34f9f 100644 --- a/include/channels.h +++ b/include/channels.h @@ -20,75 +20,60 @@ */ -#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 - */ - static Channel* ForceChan(Channel* Ptr, User* user, const std::string &privs, bool bursting, bool created); - /** 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 UserMembIter to remove, must be valid */ - CustomModeList custom_mode_params; + void DelUser(const UserMembIter& 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; @@ -116,32 +101,19 @@ class CoreExport Channel : public Extensible, public InviteBase */ 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 @@ -153,24 +125,22 @@ class CoreExport Channel : public Extensible, public InviteBase * * @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 @@ -196,7 +166,7 @@ class CoreExport Channel : public Extensible, public InviteBase * * @return This function returns pointer to a map of User pointers (CUList*). */ - const UserMembList* GetUsers(); + const UserMembList* GetUsers() const { return &userlist; } /** Returns true if the user given is on the given channel. * @param user The user to look for @@ -210,8 +180,9 @@ class CoreExport Channel : public Extensible, public InviteBase * @param src The source of the kick * @param user The user being kicked (must be on this channel) * @param reason The reason for the kick + * @param srcmemb The membership of the user who does the kick, can be NULL */ - void KickUser(User *src, User *user, const char* reason); + void KickUser(User* src, User* user, const std::string& reason, Membership* srcmemb = NULL); /** Part a user from this channel with the given reason. * If the reason field is NULL, no reason will be sent. @@ -220,16 +191,24 @@ class CoreExport Channel : public Extensible, public InviteBase */ 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(User *user, const char* cn, bool override, const char* key, bool bursting, time_t TS = 0); + 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) + */ + void 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 @@ -301,49 +280,18 @@ class CoreExport Channel : public Extensible, public InviteBase /** 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 */ 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); - /** Get the value of a users prefix on this channel. * @param user The user to look up * @return The module or core-defined value of the users prefix. @@ -357,24 +305,6 @@ class CoreExport Channel : public Extensible, public InviteBase */ 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 @@ -388,10 +318,40 @@ class CoreExport Channel : public Extensible, public InviteBase /** 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); +} diff --git a/include/command_parse.h b/include/command_parse.h index f9e3a740c..70544b0c8 100644 --- a/include/command_parse.h +++ b/include/command_parse.h @@ -20,8 +20,7 @@ */ -#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, @@ -31,18 +30,11 @@ 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); - /** 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* @@ -57,13 +49,15 @@ class CoreExport CommandParser * @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. @@ -71,44 +65,50 @@ class CoreExport CommandParser */ 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 @@ -120,23 +120,23 @@ class CoreExport CommandParser */ 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 @@ -165,5 +165,3 @@ const int duration_multi[] = 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 diff --git a/include/commands/cmd_whowas.h b/include/commands/cmd_whowas.h index d33354122..0a38b44f1 100644 --- a/include/commands/cmd_whowas.h +++ b/include/commands/cmd_whowas.h @@ -19,39 +19,13 @@ */ -#ifndef CMD_WHOWAS_H -#define CMD_WHOWAS_H -#include "modules.h" - -struct WhowasRequest : public Request -{ - /* list of available internal commands */ - enum Internals - { - WHOWAS_ADD = 1, - WHOWAS_STATS = 2, - WHOWAS_PRUNE = 3, - WHOWAS_MAINTAIN = 4 - }; - - const Internals type; - std::string value; - User* user; - - WhowasRequest(Module* src, Module* whowas, Internals Type) : Request(src, whowas, "WHOWAS"), type(Type) - {} -}; +#pragma once -/* Forward ref for timer */ -class WhoWasMaintainTimer; +#include "modules.h" /* Forward ref for typedefs */ class WhoWasGroup; -/** Timer that is used to maintain the whowas list, called once an hour - */ -extern WhoWasMaintainTimer* timer; - /** A group of users related by nickname */ typedef std::deque<WhoWasGroup*> whowas_set; @@ -72,15 +46,28 @@ typedef std::deque<std::pair<time_t,irc::string> > whowas_users_fifo; class CommandWhowas : public Command { private: - /** Whowas container, contains a map of vectors of users tracked by WHOWAS + /** Primary container, links nicknames tracked by WHOWAS to a list of records */ whowas_users whowas; - /** Whowas container, contains a map of time_t to users tracked by WHOWAS + /** List of nicknames in the order they were inserted into the map */ whowas_users_fifo whowas_fifo; public: + /** 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; + CommandWhowas(Module* parent); /** Handle command. * @param parameters The parameters to the comamnd @@ -91,8 +78,8 @@ class CommandWhowas : public Command 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); + void Prune(); + void Maintain(); ~CommandWhowas(); }; @@ -123,19 +110,4 @@ class WhoWasGroup /** 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 diff --git a/include/compat.h b/include/compat.h new file mode 100644 index 000000000..fa75cd754 --- /dev/null +++ b/include/compat.h @@ -0,0 +1,114 @@ +/* + * 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> +#else +# define TR1NS std::tr1 +# include <tr1/unordered_map> +#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" +#else +# include <unistd.h> +# define ENTRYPOINT int main(int argc, char** argv) +# define DllExport __attribute__ ((visibility ("default"))) +# define CoreExport __attribute__ ((visibility ("default"))) +#endif diff --git a/include/configparser.h b/include/configparser.h index 999d79e24..8292fdda5 100644 --- a/include/configparser.h +++ b/include/configparser.h @@ -17,6 +17,8 @@ */ +#pragma once + struct fpos { std::string filename; @@ -31,7 +33,7 @@ struct fpos enum ParseFlags { - FLAG_USE_XML = 1, + FLAG_USE_COMPAT = 1, FLAG_NO_EXEC = 2, FLAG_NO_INC = 4 }; @@ -76,5 +78,3 @@ struct FileWrapper } } }; - - diff --git a/include/configreader.h b/include/configreader.h index 1edacfe13..ec9932658 100644 --- a/include/configreader.h +++ b/include/configreader.h @@ -21,8 +21,7 @@ */ -#ifndef INSPIRCD_CONFIGREADER -#define INSPIRCD_CONFIGREADER +#pragma once #include <sstream> #include <string> @@ -45,12 +44,22 @@ class CoreExport ConfigTag : public refcountbase /** 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) @@ -59,6 +68,16 @@ class CoreExport ConfigTag : public refcountbase */ 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; } @@ -93,14 +112,18 @@ class ServerLimits 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 @@ -130,11 +153,6 @@ 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; @@ -142,8 +160,6 @@ struct CommandLineConf /** Saved argv from startup */ char** argv; - - std::string startup_log; }; class CoreExport OperInfo : public refcountbase @@ -170,11 +186,6 @@ 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. @@ -189,6 +200,32 @@ class CoreExport ServerConfig 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(CONFIG_PATH) + , Data(DATA_PATH) + , Log(LOG_PATH) + , Module(MOD_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); } + }; /** Get a configuration tag * @param tag The name of the tag to get @@ -225,6 +262,9 @@ class CoreExport ServerConfig */ ServerLimits Limits; + /** Locations of various types of file (config, module, etc). */ + ServerPaths Paths; + /** Configuration parsed from the command line. */ CommandLineConf cmdline; @@ -239,27 +279,14 @@ class CoreExport ServerConfig */ 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 @@ -272,71 +299,6 @@ class CoreExport ServerConfig */ 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; @@ -355,13 +317,6 @@ class CoreExport ServerConfig */ 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. @@ -395,6 +350,13 @@ class CoreExport ServerConfig */ 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. @@ -444,16 +406,6 @@ class CoreExport ServerConfig */ 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. */ @@ -467,27 +419,10 @@ class CoreExport ServerConfig */ 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 */ @@ -503,11 +438,14 @@ class CoreExport ServerConfig */ 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; + /** Oper types keyed by their name + */ + OperIndex OperTypes; + /** Max channels per user */ unsigned int MaxChans; @@ -529,15 +467,7 @@ class CoreExport ServerConfig /** 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 @@ -552,23 +482,13 @@ class CoreExport ServerConfig 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 */ @@ -577,11 +497,6 @@ class CoreExport ServerConfig /** 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 @@ -609,4 +524,13 @@ class CoreExport ConfigReaderThread : public Thread bool IsDone() { return done; } }; -#endif +class CoreExport ConfigStatus +{ + public: + User* const srcuser; + + ConfigStatus(User* user = NULL) + : srcuser(user) + { + } +}; diff --git a/include/consolecolors.h b/include/consolecolors.h index f7ca1335e..9b7e0670a 100644 --- a/include/consolecolors.h +++ b/include/consolecolors.h @@ -14,8 +14,8 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef CONSOLECOLORS_H -#define CONSOLECOLORS_H + +#pragma once #include <ostream> @@ -96,5 +96,3 @@ inline std::ostream& con_reset(std::ostream &s) } #endif - -#endif diff --git a/include/ctables.h b/include/ctables.h index f9cd08cb3..a69f5c86f 100644 --- a/include/ctables.h +++ b/include/ctables.h @@ -21,8 +21,7 @@ */ -#ifndef CTABLES_H -#define CTABLES_H +#pragma once /** Used to indicate command success codes */ @@ -44,7 +43,6 @@ const char FLAG_SERVERONLY = 7; // technically anything nonzero below 'A' works */ 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 */ @@ -77,10 +75,17 @@ struct RouteDescriptor */ 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 */ @@ -99,7 +104,7 @@ struct RouteDescriptor /** 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 @@ -120,10 +125,6 @@ class CoreExport Command : public ServiceProvider */ unsigned long use_count; - /** used by /stats m - */ - unsigned long total_bytes; - /** True if the command is disabled to non-opers */ bool disabled; @@ -162,20 +163,13 @@ class CoreExport Command : public ServiceProvider * @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; @@ -224,7 +218,34 @@ class CoreExport Command : public ServiceProvider 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 @@ -252,5 +273,3 @@ 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 diff --git a/include/cull_list.h b/include/cull_list.h index 75b08b7a3..ac64dced2 100644 --- a/include/cull_list.h +++ b/include/cull_list.h @@ -20,8 +20,7 @@ */ -#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 @@ -58,6 +57,3 @@ class CoreExport ActionList void Run(); }; - -#endif - diff --git a/include/dns.h b/include/dns.h deleted file mode 100644 index 27c3c8848..000000000 --- a/include/dns.h +++ /dev/null @@ -1,443 +0,0 @@ -/* - * 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" - -/** - * 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; - - /** 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) : id(i), result(res), ttl(timetolive), original(orig) { } -}; - -/** - * 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 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, 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 -}; - -/** - * 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 -}; - -/** - * 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 - diff --git a/include/dynamic.h b/include/dynamic.h index 5e66ddbb0..d42cf61bf 100644 --- a/include/dynamic.h +++ b/include/dynamic.h @@ -20,8 +20,7 @@ */ -#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. @@ -65,6 +64,3 @@ class CoreExport DLLManager : public classbase /** Get detailed version information from the module file */ std::string GetVersion(); }; - -#endif - diff --git a/include/dynref.h b/include/dynref.h new file mode 100644 index 000000000..02474b67e --- /dev/null +++ b/include/dynref.h @@ -0,0 +1,99 @@ +/* + * 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 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) {} +}; diff --git a/include/exitcodes.h b/include/exitcodes.h index d4890c94d..b1090d141 100644 --- a/include/exitcodes.h +++ b/include/exitcodes.h @@ -19,8 +19,7 @@ */ -#ifndef EXITCODE_H -#define EXITCODE_H +#pragma once /** Valid exit codes to be used with InspIRCd::Exit() */ @@ -28,30 +27,18 @@ enum ExitStatus { 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 - diff --git a/include/extensible.h b/include/extensible.h index bcc4992bb..87fe65ccb 100644 --- a/include/extensible.h +++ b/include/extensible.h @@ -17,8 +17,7 @@ */ -#ifndef EXTENSIBLE_H -#define EXTENSIBLE_H +#pragma once #include <stdint.h> @@ -85,6 +84,11 @@ class CoreExport Extensible : public classbase * 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) @@ -95,6 +99,11 @@ class CoreExport Extensible : public classbase 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 @@ -117,7 +126,7 @@ class CoreExport LocalExtItem : public ExtensionItem virtual void free(void* item) = 0; }; -template<typename T> +template <typename T, typename Del = stdalgo::defaultdeleter<T> > class SimpleExtItem : public LocalExtItem { public: @@ -138,24 +147,28 @@ class SimpleExtItem : public LocalExtItem { 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)); } }; @@ -175,6 +188,7 @@ class CoreExport LocalIntExt : public LocalExtItem 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); }; @@ -190,5 +204,3 @@ class CoreExport StringExtItem : public ExtensionItem void unset(Extensible* container); void free(void* item); }; - -#endif diff --git a/include/filelogger.h b/include/filelogger.h index 22a94c934..ce571c3ae 100644 --- a/include/filelogger.h +++ b/include/filelogger.h @@ -18,27 +18,10 @@ */ -#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 @@ -46,12 +29,9 @@ 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 - diff --git a/include/fileutils.h b/include/fileutils.h new file mode 100644 index 000000000..89f92f97f --- /dev/null +++ b/include/fileutils.h @@ -0,0 +1,87 @@ +/* + * 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); +}; diff --git a/include/hash_map.h b/include/hash_map.h deleted file mode 100644 index 1b43f0118..000000000 --- a/include/hash_map.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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. - */ - #ifndef _WIN32 - #if __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 - #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 - #endif - - // tr1: restoring sanity to our headers. now if only compiler vendors could agree on a FUCKING INCLUDE FILE. - #ifdef HAS_TR1_UNORDERED - #define hash_map unordered_map - #define nspace std::tr1 - #define BEGIN_HASHMAP_NAMESPACE namespace std { namespace tr1 { - #define END_HASHMAP_NAMESPACE } } - #endif - -#endif diff --git a/include/hashcomp.h b/include/hashcomp.h index 78d7ee878..6cd3fc3c0 100644 --- a/include/hashcomp.h +++ b/include/hashcomp.h @@ -22,8 +22,7 @@ */ -#ifndef HASHCOMP_H -#define HASHCOMP_H +#pragma once #include <cstring> #include <string> @@ -31,7 +30,7 @@ #include <deque> #include <map> #include <set> -#include "hash_map.h" +#include "inspircd.h" /******************************************************* * This file contains classes and templates that deal @@ -110,12 +109,22 @@ namespace irc 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 @@ -144,7 +153,7 @@ namespace irc * @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 @@ -152,65 +161,18 @@ namespace irc * @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: - - /** Output string - */ - std::string joined; - - 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 - */ - stringjoiner(const std::string &seperator, const std::vector<std::string> &sequence, int begin, int end); - - /** 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 - */ - stringjoiner(const std::string &seperator, const std::deque<std::string> &sequence, int begin, int end); - - /** 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 - */ - stringjoiner(const std::string &seperator, const char* const* sequence, int begin, int end); - - /** Get the joined sequence - * @return A reference to the joined string - */ - std::string& GetJoined(); - }; + std::string CoreExport stringjoiner(const std::vector<std::string>& sequence, char separator = ' '); /** irc::modestacker stacks mode sequences into a list. * It can then reproduce this list, clamped to a maximum of MAXMODES @@ -281,80 +243,6 @@ namespace irc */ 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; - } - }; - - /** irc::tokenstream reads a string formatted as per RFC1459 and RFC2812. - * It will split the string into 'tokens' each containing one parameter - * from the string. - * For instance, if it is instantiated with the string: - * "PRIVMSG #test :foo bar baz qux" - * then each successive call to tokenstream::GetToken() will return - * "PRIVMSG", "#test", "foo bar baz qux", "". - * Note that if the whole string starts with a colon this is not taken - * to mean the string is all one parameter, and the first item in the - * list will be ":item". This is to allow for parsing 'source' fields - * from data. - */ - class CoreExport tokenstream - { - 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(std::string &token); - - /** Fetch the next token from the stream as an irc::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(irc::string &token); - - /** Fetch the next token from the stream as an integer - * @param token The next token available, or undefined if none remain - * @return True if tokens are left to be read, false if the last token was just retrieved. - */ - bool GetToken(int &token); - - /** Fetch the next token from the stream as a long integer - * @param token The next token available, or undefined 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. @@ -364,43 +252,39 @@ namespace irc */ class CoreExport sepstream { - private: + protected: /** Original string. */ std::string tokens; - /** Last position of a seperator token + /** Separator value */ - std::string::iterator last_starting_position; + char sep; /** Current string position */ - std::string::iterator n; - /** Seperator value + size_t pos; + /** If set then GetToken() can return an empty string */ - char sep; + bool allow_empty; public: /** Create a sepstream and fill it with the provided data */ - sepstream(const std::string &source, char seperator); - - /** Destructor - */ - virtual ~sepstream(); + sepstream(const std::string &source, char separator, bool allowempty = false); /** 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); + bool GetToken(std::string& token); /** Fetch the entire remaining stream, without tokenizing * @return The remaining part of the stream */ - virtual const std::string GetRemaining(); + 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(); + bool StreamEnd(); }; /** A derived form of sepstream, which seperates on commas @@ -408,9 +292,9 @@ namespace irc class CoreExport commasepstream : public sepstream { public: - /** Initialize with comma seperator + /** Initialize with comma separator */ - commasepstream(const std::string &source) : sepstream(source, ',') + commasepstream(const std::string &source, bool allowempty = false) : sepstream(source, ',', allowempty) { } }; @@ -420,13 +304,57 @@ namespace irc class CoreExport spacesepstream : public sepstream { public: - /** Initialize with space seperator + /** Initialize with space separator */ - spacesepstream(const std::string &source) : sepstream(source, ' ') + spacesepstream(const std::string &source, bool allowempty = false) : sepstream(source, ' ', allowempty) { } }; + /** irc::tokenstream reads a string formatted as per RFC1459 and RFC2812. + * It will split the string into 'tokens' each containing one parameter + * from the string. + * For instance, if it is instantiated with the string: + * "PRIVMSG #test :foo bar baz qux" + * then each successive call to tokenstream::GetToken() will return + * "PRIVMSG", "#test", "foo bar baz qux", "". + * Note that if the whole string starts with a colon this is not taken + * to mean the string is all one parameter, and the first item in the + * list will be ":item". This is to allow for parsing 'source' fields + * from data. + */ + class CoreExport tokenstream : private spacesepstream + { + public: + /** Create a tokenstream and fill it with the provided data + */ + tokenstream(const std::string &source); + + /** 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(std::string &token); + + /** Fetch the next token from the stream as an irc::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(irc::string &token); + + /** Fetch the next token from the stream as an integer + * @param token The next token available, or undefined if none remain + * @return True if tokens are left to be read, false if the last token was just retrieved. + */ + bool GetToken(int &token); + + /** Fetch the next token from the stream as a long integer + * @param token The next token available, or undefined if none remain + * @return True if tokens are left to be read, false if the last token was just retrieved. + */ + bool GetToken(long &token); + }; + /** 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 @@ -481,12 +409,6 @@ namespace irc 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 @@ -590,72 +512,3 @@ inline std::string& trim(std::string &str) 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 diff --git a/include/inspircd.h b/include/inspircd.h index e2eaf8292..db13b2ab3 100644 --- a/include/inspircd.h +++ b/include/inspircd.h @@ -23,63 +23,43 @@ */ -#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 "typedefs.h" -#include "consolecolors.h" +#include "stdalgo.h" CoreExport extern InspIRCd* ServerInstance; +#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" @@ -99,43 +79,27 @@ CoreExport extern InspIRCd* ServerInstance; #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 - /** 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 @@ -260,17 +224,33 @@ class serverstats } }; -DEFINE_HANDLER2(IsNickHandler, bool, const char*, size_t); +/** 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> Lines; + +public: + /** (Re)build the ISUPPORT vector. */ + void Build(); + + /** Returns the std::vector of ISUPPORT lines. */ + const std::vector<std::string>& GetLines() + { + return this->Lines; + } + + /** Send the 005 numerics (ISUPPORT) to a user. */ + void SendTo(LocalUser* user); +}; + +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 @@ -280,10 +260,6 @@ class TestSuite; 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(); @@ -293,25 +269,6 @@ class CoreExport InspIRCd */ 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; @@ -321,8 +278,15 @@ class CoreExport InspIRCd */ 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; @@ -333,11 +297,8 @@ class CoreExport InspIRCd 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 @@ -350,28 +311,12 @@ class CoreExport InspIRCd */ 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; @@ -390,10 +335,6 @@ class CoreExport InspIRCd */ CommandParser* Parser; - /** Socket engine, handles socket activity events - */ - SocketEngine* SE; - /** Thread engine, Handles threading where required */ ThreadEngine* Threads; @@ -429,13 +370,9 @@ class CoreExport InspIRCd */ SnomaskManager* SNO; - /** DNS class, provides resolver facilities to the core and modules - */ - DNS* Res; - /** Timer manager class, triggers Timer timer events */ - TimerManager* Timers; + TimerManager Timers; /** X-Line manager. Handles G/K/Q/E line setting, removal and matching */ @@ -447,7 +384,7 @@ class CoreExport InspIRCd /** 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 */ @@ -461,13 +398,12 @@ class CoreExport InspIRCd */ ProtocolInterface* PI; - /** Holds extensible for user nickforced - */ - LocalIntExt NICKForced; - /** 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, @@ -508,15 +444,6 @@ class CoreExport InspIRCd */ 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 @@ -524,17 +451,6 @@ class CoreExport InspIRCd */ 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); @@ -545,38 +461,21 @@ class CoreExport InspIRCd */ 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 - */ - caller1<bool, const std::string&> IsSID; - - /** Rehash the local server + * @param sid string to check against */ - caller1<void, const std::string&> Rehash; + static bool IsSID(const std::string& sid); /** Handles incoming signals after being set * @param signal the signal recieved @@ -601,10 +500,13 @@ class CoreExport InspIRCd */ 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 @@ -615,56 +517,13 @@ class CoreExport InspIRCd * @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. @@ -672,8 +531,8 @@ class CoreExport InspIRCd * @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. @@ -682,30 +541,21 @@ class CoreExport InspIRCd * @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 @@ -718,36 +568,16 @@ class CoreExport InspIRCd 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 @@ -756,26 +586,14 @@ class CoreExport InspIRCd * @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 - */ - 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 + * @return True if the strings match, false if they do not */ - 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 @@ -809,16 +627,6 @@ class CoreExport InspIRCd */ 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 @@ -826,52 +634,26 @@ class CoreExport InspIRCd */ 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. - */ - void ResetMaxBans(); - /** Return a time_t as a human-readable string. */ - std::string TimeString(time_t curtime); + static std::string TimeString(time_t curtime); /** 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; @@ -885,15 +667,14 @@ class CommandModule : public Module { } - 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); +} diff --git a/include/inspsocket.h b/include/inspsocket.h index c62c5a250..720489b77 100644 --- a/include/inspsocket.h +++ b/include/inspsocket.h @@ -21,11 +21,12 @@ */ -#ifndef INSPSOCKET_H -#define INSPSOCKET_H +#pragma once #include "timer.h" +class IOHook; + /** * States which a socket may be in */ @@ -93,7 +94,7 @@ class CoreExport SocketTimeout : public Timer /** Handle tick event */ - virtual void Tick(time_t now); + virtual bool Tick(time_t now); }; /** @@ -102,8 +103,9 @@ class CoreExport SocketTimeout : public Timer */ 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; @@ -114,10 +116,10 @@ class CoreExport StreamSocket : public EventHandler 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 */ @@ -229,9 +231,6 @@ class CoreExport BufferedSocket : public StreamSocket 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; } diff --git a/include/inspstring.h b/include/inspstring.h index a6ef5e552..ccc77da66 100644 --- a/include/inspstring.h +++ b/include/inspstring.h @@ -18,40 +18,38 @@ */ -#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()); +} diff --git a/include/intrusive_list.h b/include/intrusive_list.h new file mode 100644 index 000000000..399dc33e8 --- /dev/null +++ b/include/intrusive_list.h @@ -0,0 +1,162 @@ +/* + * 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> + +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> +struct 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>; +}; + +template <typename T, typename Tag> +class intrusive_list +{ + 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; + } + + void 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; + + intrusive_list() + : listhead(NULL) + , 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; + } + listhead = x; + } + + void erase(const iterator& it) + { + erase(*it); + } + + void erase(T* x) + { + if (listhead == x) + listhead = x->intrusive_list_node<T, Tag>::ptr_next; + x->intrusive_list_node<T, Tag>::unlink(); + listsize--; + } + + private: + T* listhead; + size_t listsize; +}; diff --git a/include/iohook.h b/include/iohook.h new file mode 100644 index 000000000..ce7ca2a1b --- /dev/null +++ b/include/iohook.h @@ -0,0 +1,89 @@ +/* + * 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; +}; diff --git a/include/listmode.h b/include/listmode.h new file mode 100644 index 000000000..75385588b --- /dev/null +++ b/include/listmode.h @@ -0,0 +1,206 @@ +/* + * 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 stack The mode stack to add the mode change to + */ + virtual void RemoveMode(Channel* channel, irc::modestacker& stack); + + /** Perform a rehash of this mode's configuration data + */ + virtual 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; +} diff --git a/include/logger.h b/include/logger.h index 0fa4bc7cd..2ea280be8 100644 --- a/include/logger.h +++ b/include/logger.h @@ -18,8 +18,18 @@ */ -#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. */ @@ -77,9 +87,11 @@ class CoreExport FileWriter 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) { } @@ -91,13 +103,13 @@ class CoreExport LogStream : public classbase /** 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; @@ -127,7 +139,6 @@ class CoreExport LogManager FileLogMap FileLogs; public: - LogManager(); ~LogManager(); @@ -199,17 +210,15 @@ class CoreExport 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 diff --git a/include/membership.h b/include/membership.h index 436a9371c..44eaf1eb6 100644 --- a/include/membership.h +++ b/include/membership.h @@ -1,6 +1,7 @@ /* * 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 @@ -17,10 +18,9 @@ */ -#ifndef MEMBERSHIP_H -#define MEMBERSHIP_H +#pragma once -class CoreExport Membership : public Extensible +class CoreExport Membership : public Extensible, public intrusive_list_node<Membership> { public: User* const user; @@ -33,12 +33,39 @@ class CoreExport Membership : public Extensible return modes.find(m) != std::string::npos; } 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; + intrusive_list<Invitation, T> invites; public: void ClearInvites(); @@ -46,7 +73,7 @@ class CoreExport InviteBase friend class Invitation; }; -class Invitation : public classbase +class CoreExport Invitation : public intrusive_list_node<Invitation, Channel>, public intrusive_list_node<Invitation, LocalUser> { Invitation(Channel* c, LocalUser* u, time_t timeout) : user(u), chan(c), expiry(timeout) {} @@ -60,4 +87,15 @@ class Invitation : public classbase static Invitation* Find(Channel* c, LocalUser* u, bool check_expired = true); }; -#endif +typedef intrusive_list<Invitation, LocalUser> InviteList; + +template<typename T> +inline void InviteBase<T>::ClearInvites() +{ + for (typename intrusive_list<Invitation, T>::iterator i = invites.begin(); i != invites.end(); ) + { + Invitation* inv = *i; + ++i; + delete inv; + } +} diff --git a/include/mode.h b/include/mode.h index 1dab442d4..7c5682135 100644 --- a/include/mode.h +++ b/include/mode.h @@ -20,8 +20,7 @@ */ -#ifndef MODE_H -#define MODE_H +#pragma once #include "ctables.h" @@ -47,17 +46,6 @@ enum ModeAction }; /** - * 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 * '$', and you dont know what $ means, then you can compare it to these three values to determine @@ -85,6 +73,10 @@ enum ParamSpec 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 @@ -101,6 +93,22 @@ enum ParamSpec */ class CoreExport ModeHandler : public ServiceProvider { + 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 + */ + Id modeid; + protected: /** * The mode parameter translation type @@ -116,10 +124,6 @@ class CoreExport ModeHandler : public ServiceProvider */ char mode; - /** Mode prefix, or 0 - */ - char prefix; - /** * True if the mode requires oper status * to set. @@ -144,6 +148,10 @@ class CoreExport ModeHandler : public ServiceProvider */ 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 */ @@ -159,28 +167,34 @@ class CoreExport ModeHandler : public ServiceProvider * @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 + */ + ListModeBase* IsListModeBase(); + + /** + * Check whether this mode handler inherits from ListModeBase + * @return non-NULL if this mode handler inherits from ParamModeBase, NULL otherwise */ - virtual unsigned int GetPrefixRank(); + ParamModeBase* IsParameterMode(); + /** * Returns the mode's type */ @@ -206,6 +220,11 @@ class CoreExport ModeHandler : public ServiceProvider */ 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); @@ -269,28 +288,102 @@ class CoreExport ModeHandler : public ServiceProvider 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 */ - virtual void RemoveMode(Channel* channel, irc::modestacker* stack = NULL); + virtual void RemoveMode(Channel* channel, irc::modestacker& stack); 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 + */ + PrefixMode(Module* Creator, const std::string& Name, char ModeLetter); + + /** + * 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 stack The mode stack to add the mode change to + */ + void RemoveMode(Channel* chan, irc::modestacker& stack); + + /** + * 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 @@ -303,7 +396,6 @@ class CoreExport SimpleUserModeHandler : public ModeHandler 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); }; @@ -317,20 +409,9 @@ class CoreExport SimpleChannelModeHandler : public ModeHandler 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, @@ -339,11 +420,12 @@ class CoreExport ParamChannelModeHandler : public ModeHandler */ 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) */ @@ -354,17 +436,18 @@ class CoreExport ModeWatcher : public classbase /** * 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) @@ -380,11 +463,10 @@ class CoreExport ModeWatcher : public classbase * 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 @@ -393,12 +475,11 @@ class CoreExport ModeWatcher : public classbase * @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, @@ -407,18 +488,50 @@ typedef std::vector<ModeWatcher*>::iterator ModeWatchIter; */ class CoreExport 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 + */ + 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 */ - std::vector<ModeWatcher*> modewatchers[256]; + std::multimap<std::string, ModeWatcher*> modewatchermap; + /** Displays the current modes of a channel or user. * Used by ModeParser::Process. */ @@ -433,6 +546,26 @@ class CoreExport ModeParser */ ModeAction TryMode(User* user, User* targu, Channel* targc, bool adding, unsigned char mode, std::string ¶m, 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. */ @@ -444,17 +577,44 @@ class CoreExport ModeParser 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 mode change won't be handed over to + * the linking module to be sent to other servers, but will be processed + * locally and sent to local user(s) as usual. + */ + MODE_LOCALONLY = 2 + }; - /** 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. * @@ -474,13 +634,15 @@ class CoreExport ModeParser * 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::string& GetLastParse() const { return LastParse; } const std::vector<std::string>& GetLastParseParams() { return LastParseParams; } const std::vector<TranslateType>& GetLastParseTranslate() { return LastParseTranslate; } + /** 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 @@ -496,9 +658,9 @@ class CoreExport ModeParser * 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 @@ -510,12 +672,18 @@ class CoreExport ModeParser /** 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? + * @param user The source of the mode change, can be a server user. + * @param flags Optional flags controlling how the mode change is processed, + * defaults to MODE_NONE. + */ + void Process(const std::vector<std::string>& parameters, User* user, ModeProcessFlag flags = MODE_NONE); + + /** 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. */ - void Process(const std::vector<std::string>& parameters, User *user, bool merge = false); + 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 @@ -524,27 +692,26 @@ class CoreExport ModeParser */ 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 @@ -552,14 +719,46 @@ class CoreExport ModeParser * 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]; } }; -#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); +} diff --git a/include/modes/cmode_b.h b/include/modes/cmode_b.h deleted file mode 100644 index afd5cd13b..000000000 --- a/include/modes/cmode_b.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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); -}; - diff --git a/include/modes/cmode_k.h b/include/modes/cmode_k.h deleted file mode 100644 index 000667f72..000000000 --- a/include/modes/cmode_k.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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); -}; diff --git a/include/modes/cmode_l.h b/include/modes/cmode_l.h deleted file mode 100644 index 3018a0d67..000000000 --- a/include/modes/cmode_l.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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); -}; diff --git a/include/modes/cmode_v.h b/include/modes/cmode_v.h deleted file mode 100644 index ab037f33f..000000000 --- a/include/modes/cmode_v.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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); -}; - diff --git a/include/modes/simplemodes.h b/include/modes/simplemodes.h deleted file mode 100644 index 661bba400..000000000 --- a/include/modes/simplemodes.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * 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') - { - } -}; diff --git a/include/modes/umode_s.h b/include/modes/umode_s.h deleted file mode 100644 index 8ac8fa31a..000000000 --- a/include/modes/umode_s.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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); -}; diff --git a/include/modules.h b/include/modules.h index c0d852b09..9c0b22b6c 100644 --- a/include/modules.h +++ b/include/modules.h @@ -23,8 +23,7 @@ */ -#ifndef MODULES_H -#define MODULES_H +#pragma once #include "dynamic.h" #include "base.h" @@ -35,7 +34,6 @@ #include <sstream> #include "timer.h" #include "mode.h" -#include "dns.h" /** Used to define a set of behavior bits for a module */ @@ -109,35 +107,33 @@ struct ModResult { /** 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 7 +#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); @@ -149,21 +145,19 @@ struct ModResult { */ #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) @@ -212,41 +206,6 @@ class CoreExport Version 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 @@ -282,38 +241,6 @@ class CoreExport DataProvider : public ServiceProvider : 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 }; @@ -323,21 +250,21 @@ 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 }; @@ -349,6 +276,11 @@ enum Implementation */ 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 */ @@ -387,6 +319,13 @@ class CoreExport Module : public classbase, public usecountbase { } + /** 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 @@ -477,14 +416,6 @@ class CoreExport Module : public classbase, public usecountbase */ 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') @@ -514,7 +445,7 @@ class CoreExport Module : public classbase, public usecountbase * @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 @@ -611,30 +542,10 @@ class CoreExport Module : public classbase, public usecountbase * @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 @@ -645,7 +556,7 @@ class CoreExport Module : public classbase, public usecountbase * * 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 @@ -668,25 +579,14 @@ class CoreExport Module : public classbase, public usecountbase * @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 @@ -699,68 +599,46 @@ class CoreExport Module : public classbase, public usecountbase 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. + * The modes vector contains the remainder of the mode string after the target, + * e.g.: "+wsi" or ["+ooo", "nick1", "nick2", "nick3"]. * @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 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 modes 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 - */ - virtual void OnGetServerDescription(const std::string &servername,std::string &description); + virtual void OnMode(User* user, User* usertarget, Channel* chantarget, const std::vector<std::string>& modes, const std::vector<TranslateType>& translate); /** 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. @@ -770,43 +648,6 @@ class CoreExport Module : public classbase, public usecountbase */ 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 @@ -885,9 +726,9 @@ class CoreExport Module : public classbase, public usecountbase /** 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, @@ -904,14 +745,6 @@ class CoreExport Module : public classbase, public usecountbase */ 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, @@ -976,7 +809,7 @@ class CoreExport Module : public classbase, public usecountbase * @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. @@ -1020,15 +853,14 @@ class CoreExport Module : public classbase, public usecountbase * 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 @@ -1128,12 +960,6 @@ class CoreExport Module : public classbase, public usecountbase */ 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). @@ -1162,30 +988,6 @@ class CoreExport Module : public classbase, public usecountbase */ 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() @@ -1195,48 +997,6 @@ class CoreExport Module : public classbase, public usecountbase */ 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. @@ -1273,10 +1033,12 @@ class CoreExport Module : public classbase, public usecountbase */ 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 @@ -1290,9 +1052,10 @@ class CoreExport Module : public classbase, public usecountbase * @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. @@ -1301,173 +1064,6 @@ class CoreExport Module : public classbase, public usecountbase 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; @@ -1481,15 +1077,14 @@ typedef IntModuleList::iterator EventHandlerIter; */ class CoreExport 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 */ @@ -1503,7 +1098,18 @@ class CoreExport ModuleManager /** 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. @@ -1513,6 +1119,16 @@ class CoreExport ModuleManager /** 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(); @@ -1585,6 +1201,11 @@ class CoreExport 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 */ @@ -1616,14 +1237,6 @@ class CoreExport ModuleManager 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 @@ -1637,6 +1250,11 @@ class CoreExport ModuleManager /** 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++) @@ -1653,13 +1271,10 @@ class CoreExport ModuleManager 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 @@ -1689,11 +1304,7 @@ struct AllModuleList { }; #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 @@ -1726,11 +1337,9 @@ struct AllModuleList { { \ return new y; \ } \ - extern "C" const char inspircd_src_version[] = VERSION " r" REVISION; + extern "C" DllExport const char inspircd_src_version[] = VERSION " " REVISION; #endif #define COMMAND_INIT(c) MODULE_INIT(CommandModule<c>) #endif - -#endif diff --git a/src/modules/account.h b/include/modules/account.h index ba671ba0b..c00b044e4 100644 --- a/src/modules/account.h +++ b/include/modules/account.h @@ -17,8 +17,7 @@ */ -#ifndef ACCOUNT_H -#define ACCOUNT_H +#pragma once #include <map> #include <string> @@ -40,5 +39,3 @@ inline AccountExtItem* GetAccountExtItem() { return static_cast<AccountExtItem*>(ServerInstance->Extensions.GetItem("accountname")); } - -#endif diff --git a/src/modules/m_cap.h b/include/modules/cap.h index 409671f48..1b33e05bb 100644 --- a/src/modules/m_cap.h +++ b/include/modules/cap.h @@ -18,8 +18,7 @@ */ -#ifndef M_CAP_H -#define M_CAP_H +#pragma once class CapEvent : public Event { @@ -46,7 +45,6 @@ class GenericCap 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) @@ -88,5 +86,3 @@ class GenericCap } } }; - -#endif diff --git a/include/modules/dns.h b/include/modules/dns.h new file mode 100644 index 000000000..c76c53805 --- /dev/null +++ b/include/modules/dns.h @@ -0,0 +1,193 @@ +/* + * 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), ServerInstance->Time()) + , 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 diff --git a/src/modules/hash.h b/include/modules/hash.h index f7bf85e20..da04c45ba 100644 --- a/src/modules/hash.h +++ b/include/modules/hash.h @@ -17,8 +17,7 @@ */ -#ifndef HASH_H -#define HASH_H +#pragma once #include "modules.h" @@ -40,20 +39,6 @@ class HashProvider : public DataProvider 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) { @@ -71,6 +56,3 @@ class HashProvider : public DataProvider return sum(hmac1); } }; - -#endif - diff --git a/src/modules/httpd.h b/include/modules/httpd.h index 56fd22da0..86234d53f 100644 --- a/src/modules/httpd.h +++ b/include/modules/httpd.h @@ -21,10 +21,9 @@ */ -#include "base.h" +#pragma once -#ifndef HTTPD_H -#define HTTPD_H +#include "base.h" #include <string> #include <sstream> @@ -178,30 +177,63 @@ class HTTPRequest : public Event } }; -/** 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. +/** 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 Request +class HTTPDocumentResponse { public: + /** Module that generated this reply + */ + Module* const module; + std::stringstream* document; - int responsecode; + unsigned int responsecode; + + /** Any extra headers to include with the defaults + */ 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 + /** 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. - * @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) + HTTPDocumentResponse(Module* mod, HTTPRequest& req, std::stringstream* doc, unsigned int response) + : module(mod), document(doc), responsecode(response), src(req) { } }; -#endif +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") + { + } +}; diff --git a/include/modules/ldap.h b/include/modules/ldap.h new file mode 100644 index 000000000..75ab16077 --- /dev/null +++ b/include/modules/ldap.h @@ -0,0 +1,199 @@ +/* + * 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; +}; diff --git a/src/modules/m_regex.h b/include/modules/regex.h index 0233f938a..5ef00cdd0 100644 --- a/src/modules/m_regex.h +++ b/include/modules/regex.h @@ -18,26 +18,22 @@ */ -#ifndef M_REGEX_H -#define M_REGEX_H +#pragma once #include "inspircd.h" class Regex : public classbase { protected: - std::string regex_string; // The raw uncompiled regex string. + /** 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) - { - } + Regex(const std::string& rx) : regex_string(rx) { } public: - virtual ~Regex() - { - } + virtual ~Regex() { } virtual bool Matches(const std::string& text) = 0; @@ -50,9 +46,17 @@ public: class RegexFactory : public DataProvider { public: - RegexFactory(Module* Creator, const std::string& Name) : DataProvider(Creator, Name) {} + RegexFactory(Module* Creator, const std::string& Name) : DataProvider(Creator, Name) { } virtual Regex* Create(const std::string& expr) = 0; }; -#endif +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) { } +}; diff --git a/src/modules/sasl.h b/include/modules/sasl.h index f67351104..321711a68 100644 --- a/src/modules/sasl.h +++ b/include/modules/sasl.h @@ -17,8 +17,7 @@ */ -#ifndef SASL_H -#define SASL_H +#pragma once class SASLFallback : public Event { @@ -30,5 +29,3 @@ class SASLFallback : public Event Send(); } }; - -#endif diff --git a/src/modules/spanningtree.h b/include/modules/spanningtree.h index 212f35ff3..99f4f9fc4 100644 --- a/src/modules/spanningtree.h +++ b/include/modules/spanningtree.h @@ -17,8 +17,7 @@ */ -#ifndef SPANNINGTREE_H -#define SPANNINGTREE_H +#pragma once struct AddServerEvent : public Event { @@ -39,5 +38,3 @@ struct DelServerEvent : public Event Send(); } }; - -#endif diff --git a/src/modules/sql.h b/include/modules/sql.h index 436cd1da8..3f378d8b8 100644 --- a/src/modules/sql.h +++ b/include/modules/sql.h @@ -17,8 +17,7 @@ */ -#ifndef INSPIRCD_SQLAPI_3 -#define INSPIRCD_SQLAPI_3 +#pragma once /** Defines the error types which SQLerror may be set to */ @@ -179,9 +178,7 @@ class SQLProvider : public DataProvider userinfo["ip"] = user->GetIPString(); userinfo["gecos"] = user->fullname; userinfo["ident"] = user->ident; - userinfo["server"] = user->server; + userinfo["server"] = user->server->GetName(); userinfo["uuid"] = user->uuid; } }; - -#endif diff --git a/src/modules/ssl.h b/include/modules/ssl.h index 9deafb830..0f58e0b7b 100644 --- a/src/modules/ssl.h +++ b/include/modules/ssl.h @@ -18,11 +18,10 @@ */ -#ifndef SSL_H -#define SSL_H +#pragma once -#include <map> #include <string> +#include "iohook.h" /** ssl_cert is a class which abstracts SSL certificate * and key information. @@ -132,44 +131,116 @@ class ssl_cert : public refcountbase } }; -/** Get certificate from a socket (only useful with an SSL module) */ -struct SocketCertificateRequest : public Request +class SSLIOHook : public IOHook { - StreamSocket* const sock; - ssl_cert* cert; + protected: + /** Peer SSL certificate, set by the SSL module + */ + reference<ssl_cert> certificate; + + public: + SSLIOHook(IOHookProvider* hookprov) + : IOHook(hookprov) + { + } - SocketCertificateRequest(StreamSocket* ss, Module* Me) - : Request(Me, ss->GetIOHook(), "GET_SSL_CERT"), sock(ss), cert(NULL) + /** + * 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 { - Send(); + return certificate; } - std::string GetFingerprint() + /** + * 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 ""; } }; -/** Get certificate from a user (requires m_sslinfo) */ -struct UserCertificateRequest : public Request +/** Helper functions for obtaining SSL client certificates and key fingerprints + * from StreamSockets + */ +class SSLClientCert { - User* const user; - ssl_cert* cert; + 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 ""; + } +}; - UserCertificateRequest(User* u, Module* Me, Module* info = ServerInstance->Modules->Find("m_sslinfo.so")) - : Request(Me, info, "GET_USER_CERT"), user(u), cert(NULL) +class UserCertificateAPIBase : public DataProvider +{ + public: + UserCertificateAPIBase(Module* parent) + : DataProvider(parent, "m_sslinfo_api") { - Send(); } - std::string GetFingerprint() + /** 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 ""; } }; -#endif +/** 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") + { + } +}; diff --git a/include/numerics.h b/include/numerics.h index 4fce4cb6d..2418730d2 100644 --- a/include/numerics.h +++ b/include/numerics.h @@ -18,13 +18,9 @@ */ -#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! @@ -44,76 +40,111 @@ enum Numerics /* * 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.. @@ -131,31 +162,37 @@ enum 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 diff --git a/include/parammode.h b/include/parammode.h new file mode 100644 index 000000000..b0005262e --- /dev/null +++ b/include/parammode.h @@ -0,0 +1,75 @@ +/* + * 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); + } +}; diff --git a/include/protocol.h b/include/protocol.h index aabb5b022..01eb145f1 100644 --- a/include/protocol.h +++ b/include/protocol.h @@ -18,8 +18,7 @@ */ -#ifndef PROTOCOL_H -#define PROTOCOL_H +#pragma once #include "hashcomp.h" @@ -27,40 +26,77 @@ class User; 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 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(Channel* chan, const std::string& key, const std::string& data) { } - /** Send metadata for an object to other linked servers. - * @param target The object to send metadata for. + /** 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(Extensible* target, const std::string &key, const std::string &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. @@ -69,33 +105,19 @@ class ProtocolInterface 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 source The source of the mode change + * @param usertarget The target user, NULL if the target is a channel + * @param chantarget The target channel, NULL if the target is a user * @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); - } + virtual void SendMode(User* source, User* usertarget, Channel* chantarget, const parameterlist& modedata, const std::vector<TranslateType>& translate) { } /** 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. @@ -107,34 +129,39 @@ class ProtocolInterface * @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 - diff --git a/include/server.h b/include/server.h new file mode 100644 index 000000000..3d8854734 --- /dev/null +++ b/include/server.h @@ -0,0 +1,68 @@ +/* + * 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 + */ + const 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; + + 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; } +}; diff --git a/include/snomasks.h b/include/snomasks.h index 85ad26f71..df4faab47 100644 --- a/include/snomasks.h +++ b/include/snomasks.h @@ -20,31 +20,52 @@ */ -#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. @@ -53,9 +74,9 @@ class Snomask */ class CoreExport SnomaskManager { - public: Snomask masks[26]; + public: /** Create a new SnomaskManager */ SnomaskManager(); @@ -95,7 +116,6 @@ class CoreExport 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 @@ -105,6 +125,12 @@ class CoreExport SnomaskManager * 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; +}; diff --git a/include/socket.h b/include/socket.h index 5f6705124..c292b7010 100644 --- a/include/socket.h +++ b/include/socket.h @@ -22,8 +22,7 @@ */ -#ifndef INSPIRCD_SOCKET_H -#define INSPIRCD_SOCKET_H +#pragma once #ifndef _WIN32 @@ -110,9 +109,6 @@ namespace irc */ 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 @@ -128,16 +124,10 @@ namespace irc * @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 @@ -151,6 +141,12 @@ class CoreExport ListenSocket : public EventHandler 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); @@ -164,7 +160,10 @@ class CoreExport ListenSocket : public EventHandler /** 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(); +}; diff --git a/include/socketengine.h b/include/socketengine.h index 37b7d6373..3a15e98c1 100644 --- a/include/socketengine.h +++ b/include/socketengine.h @@ -20,13 +20,12 @@ */ -#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" @@ -128,7 +127,7 @@ enum EventMask /** 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. @@ -137,7 +136,7 @@ enum EventMask /** 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 @@ -166,6 +165,9 @@ class CoreExport EventHandler : public classbase 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 @@ -231,34 +233,79 @@ class CoreExport EventHandler : public classbase */ 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. @@ -266,14 +313,16 @@ public: * 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 @@ -282,7 +331,7 @@ public: * @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 @@ -295,17 +344,17 @@ public: * @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 */ - 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 @@ -315,21 +364,21 @@ public: * 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 @@ -339,23 +388,17 @@ public: * 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. @@ -364,21 +407,20 @@ public: * @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. @@ -388,7 +430,7 @@ public: * @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. @@ -398,7 +440,7 @@ public: * @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. @@ -410,7 +452,7 @@ public: * @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. @@ -418,11 +460,11 @@ public: * @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. @@ -431,19 +473,19 @@ public: * @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. @@ -451,29 +493,29 @@ public: * @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 @@ -484,11 +526,11 @@ public: * @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 @@ -516,8 +558,3 @@ inline bool SocketEngine::IgnoreError() return false; } - -SocketEngine* CreateSocketEngine(); - -#endif - diff --git a/include/stdalgo.h b/include/stdalgo.h new file mode 100644 index 000000000..afbd763fb --- /dev/null +++ b/include/stdalgo.h @@ -0,0 +1,97 @@ +/* + * 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>()); + } +} diff --git a/include/testsuite.h b/include/testsuite.h index f91e508c9..c760513f8 100644 --- a/include/testsuite.h +++ b/include/testsuite.h @@ -16,12 +16,12 @@ */ -#ifndef TESTSUITE_H -#define TESTSUITE_H +#pragma once + +#ifdef INSPIRCD_ENABLE_TESTSUITE class TestSuite { - bool RealGenerateUIDTests(); public: TestSuite(); ~TestSuite(); diff --git a/include/threadengine.h b/include/threadengine.h index 4bf5a48f3..39f150566 100644 --- a/include/threadengine.h +++ b/include/threadengine.h @@ -18,13 +18,12 @@ */ -#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; @@ -172,6 +171,3 @@ class CoreExport SocketThread : public Thread */ virtual void OnNotify() = 0; }; - -#endif - diff --git a/include/threadengines/threadengine_pthread.h b/include/threadengines/threadengine_pthread.h index 5168ed238..253e8d223 100644 --- a/include/threadengines/threadengine_pthread.h +++ b/include/threadengines/threadengine_pthread.h @@ -18,8 +18,7 @@ */ -#ifndef THREADENGINE_PTHREAD_H -#define THREADENGINE_PTHREAD_H +#pragma once #include <pthread.h> #include "typedefs.h" @@ -153,6 +152,3 @@ class ThreadSignalData public: ThreadSignalSocket* sock; }; - - -#endif diff --git a/include/threadengines/threadengine_win32.h b/include/threadengines/threadengine_win32.h index f068ac707..59848bd44 100644 --- a/include/threadengines/threadengine_win32.h +++ b/include/threadengines/threadengine_win32.h @@ -18,10 +18,9 @@ */ -#ifndef THREADENGINE_WIN32_H -#define THREADENGINE_WIN32_H +#pragma once -#include "inspircd_config.h" +#include "config.h" #include "base.h" class Thread; @@ -152,6 +151,3 @@ class ThreadSignalData connFD = -1; } }; - -#endif - diff --git a/include/timer.h b/include/timer.h index 9bb7128b8..503fa82a2 100644 --- a/include/timer.h +++ b/include/timer.h @@ -19,8 +19,9 @@ */ -#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 @@ -29,61 +30,71 @@ * 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) + Timer(unsigned int secs_from_now, time_t now, bool repeating = false) { trigger = now + secs_from_now; secs = secs_from_now; repeat = repeating; } - /** 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; } @@ -91,7 +102,7 @@ class CoreExport Timer /** Returns the interval (number of seconds between ticks) * of this timer object. */ - long GetSecs() + unsigned int GetInterval() const { return secs; } @@ -99,12 +110,6 @@ class CoreExport Timer /** 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() { @@ -112,6 +117,7 @@ class CoreExport Timer } }; +typedef std::multimap<time_t, Timer*> TimerMap; /** 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 @@ -119,17 +125,11 @@ class CoreExport Timer */ class CoreExport TimerManager { - protected: /** 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 */ @@ -140,15 +140,8 @@ class CoreExport TimerManager */ 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 - diff --git a/include/typedefs.h b/include/typedefs.h index 06f704120..336084c55 100644 --- a/include/typedefs.h +++ b/include/typedefs.h @@ -19,81 +19,63 @@ */ -#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 +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 holding local users, this is the type of UserManager::local_users */ -typedef std::list<LocalUser*> LocalUserList; +typedef intrusive_list<LocalUser> LocalUserList; /** 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; +typedef intrusive_list<Membership> UserChanList; /** Shorthand for an iterator into a UserChanList */ typedef UserChanList::iterator UCListIter; -/** Holds a complete ban list - */ -typedef std::vector<BanItem> BanList; - -/** A list of custom modes parameters on a channel +/** List of channels to consider when building the neighbor list of a user */ -typedef std::map<char,std::string> CustomModeList; +typedef std::vector<Membership*> IncludeChanList; /** A cached text file stored with its contents as lines */ @@ -120,7 +102,7 @@ typedef std::map<std::string, file_cache> ConfigFileCache; /** A hash of commands used by the core */ -typedef nspace::hash_map<std::string,Command*> Commandtable; +typedef TR1NS::unordered_map<std::string, Command*> Commandtable; /** Membership list of a channel */ typedef std::map<User*, Membership*> UserMembList; @@ -159,7 +141,3 @@ typedef XLineContainer::iterator ContainerIter; /** An interator in an XLineLookup */ typedef XLineLookup::iterator LookupIter; - - -#endif - diff --git a/include/uid.h b/include/uid.h index 17061bdee..772c8a716 100644 --- a/include/uid.h +++ b/include/uid.h @@ -16,12 +16,44 @@ * 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); +}; diff --git a/include/usermanager.h b/include/usermanager.h index ac8ae1cb3..a419916f2 100644 --- a/include/usermanager.h +++ b/include/usermanager.h @@ -17,42 +17,54 @@ */ -#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 { + 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; + private: - /** Map of local ip addresses for clone counting + /** Map of IP addresses for clone counting + */ + CloneMap clonemap; + + /** A CloneCounts that contains zero for both local and global */ - clonemap local_clones; + const CloneCounts zeroclonecounts; + 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; + user_hash uuidlist; /** Local client list, a list containing only local clients */ @@ -60,21 +72,28 @@ class CoreExport UserManager /** 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, @@ -90,20 +109,15 @@ class CoreExport UserManager /** 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 = ""); + void QuitUser(User* user, const std::string& quitreason, const std::string* operreason = NULL); - /** Add a user to the local clone map + /** Add a user to the clone map * @param user The user to add */ - void AddLocalClone(User *user); - - /** Add a user to the global 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 @@ -112,61 +126,52 @@ class CoreExport UserManager */ 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()); } - - /** Number of users with a certain mode set on them + /** Get a hash map containing all users, keyed by their nickname + * @return A hash map mapping nicknames to User pointers */ - int ModeCount(const char mode); + user_hash& GetUsers() { return clientlist; } /** 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 diff --git a/include/users.h b/include/users.h index 88abfbcd1..0f8154bef 100644 --- a/include/users.h +++ b/include/users.h @@ -22,12 +22,10 @@ */ -#ifndef USERS_H -#define USERS_H +#pragma once #include "socket.h" #include "inspsocket.h" -#include "dns.h" #include "mode.h" #include "membership.h" @@ -42,19 +40,6 @@ enum ClassTypes { 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? */ @@ -146,6 +131,10 @@ struct CoreExport ConnectClass : public refcountbase */ 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); @@ -250,6 +239,14 @@ class CoreExport User : public Extensible */ 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: /** Hostname of connection. @@ -267,10 +264,6 @@ class CoreExport User : public Extensible */ 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. @@ -302,18 +295,6 @@ class CoreExport User : public Extensible */ 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. */ @@ -325,7 +306,7 @@ class CoreExport User : public Extensible /** 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. @@ -333,7 +314,7 @@ class CoreExport User : public Extensible 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; @@ -347,16 +328,6 @@ class CoreExport User : public Extensible */ 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. @@ -364,27 +335,13 @@ class CoreExport User : public Extensible */ 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 */ @@ -400,13 +357,7 @@ class CoreExport User : public Extensible /** 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 @@ -428,18 +379,17 @@ class CoreExport User : public Extensible */ 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 @@ -447,12 +397,6 @@ class CoreExport User : public Extensible */ 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 */ @@ -463,12 +407,16 @@ class CoreExport User : public Extensible * @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 @@ -497,12 +445,6 @@ class CoreExport User : public Extensible */ 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 @@ -515,10 +457,6 @@ class CoreExport User : public Extensible */ 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. */ @@ -531,7 +469,7 @@ class CoreExport User : public Extensible * @param newnick The nickname to change to * @return True if the nickchange was successful. */ - inline bool ForceNickChange(const char* newnick) { return ChangeNick(newnick, true); } + bool ForceNickChange(const std::string& newnick, time_t newts = 0) { return ChangeNick(newnick, true, newts); } /** Oper down. * This will clear the +o usermode and unset the user's oper type @@ -563,6 +501,17 @@ class CoreExport User : public Extensible */ 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); @@ -580,19 +529,6 @@ class CoreExport User : public Extensible */ 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? @@ -605,12 +541,6 @@ class CoreExport User : public Extensible */ 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 @@ -619,10 +549,10 @@ class CoreExport User : public Extensible 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. */ @@ -638,32 +568,24 @@ class CoreExport User : public Extensible */ 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, @@ -672,49 +594,20 @@ class CoreExport User : public Extensible * @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) * @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, bool force = false, 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(); @@ -739,7 +632,7 @@ class CoreExport UserIOHandler : public StreamSocket typedef unsigned int already_sent_t; -class CoreExport LocalUser : public User, public InviteBase +class CoreExport LocalUser : public User, public InviteBase<LocalUser>, public intrusive_list_node<LocalUser> { public: LocalUser(int fd, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server); @@ -747,10 +640,6 @@ class CoreExport LocalUser : public User, public InviteBase UserIOHandler eh; - /** Position in UserManager::local_users - */ - LocalUserList::iterator localuseriter; - /** Stats counter for bytes inbound */ unsigned int bytes_in; @@ -777,11 +666,14 @@ class CoreExport LocalUser : public User, public InviteBase */ 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. */ @@ -792,10 +684,28 @@ class CoreExport LocalUser : public User, public InviteBase */ 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 */ @@ -804,15 +714,11 @@ class CoreExport LocalUser : public User, public InviteBase 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. @@ -839,23 +745,18 @@ class CoreExport LocalUser : public User, public InviteBase 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(); @@ -890,7 +791,7 @@ class CoreExport LocalUser : public User, public InviteBase 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); @@ -899,9 +800,15 @@ class CoreExport RemoteUser : public User 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(); @@ -926,42 +833,20 @@ inline FakeUser* IS_SERVER(User* u) { 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; +} diff --git a/include/xline.h b/include/xline.h index 2a49d8b80..fe044d0f2 100644 --- a/include/xline.h +++ b/include/xline.h @@ -20,8 +20,7 @@ */ -#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 @@ -101,16 +100,16 @@ class CoreExport XLine : public classbase * 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. */ @@ -177,9 +176,7 @@ class CoreExport KLine : public XLine virtual void Apply(User* u); - virtual void DisplayExpiry(); - - virtual const char* Displayable(); + virtual const std::string& Displayable(); virtual bool IsBurstable(); @@ -225,9 +222,7 @@ class CoreExport GLine : public XLine virtual void Apply(User* u); - virtual void DisplayExpiry(); - - virtual const char* Displayable(); + virtual const std::string& Displayable(); /** Ident mask (ident part only) */ @@ -269,11 +264,9 @@ class CoreExport ELine : public XLine virtual void Unset(); - virtual void DisplayExpiry(); - virtual void OnAdd(); - virtual const char* Displayable(); + virtual const std::string& Displayable(); /** Ident mask (ident part only) */ @@ -314,9 +307,7 @@ class CoreExport ZLine : public XLine virtual void Apply(User* u); - virtual void DisplayExpiry(); - - virtual const char* Displayable(); + virtual const std::string& Displayable(); /** IP mask (no ident part) */ @@ -351,9 +342,7 @@ class CoreExport QLine : public XLine virtual void Apply(User* u); - virtual void DisplayExpiry(); - - virtual const char* Displayable(); + virtual const std::string& Displayable(); /** Nickname mask */ @@ -536,5 +525,3 @@ class CoreExport XLineManager */ void InvokeStats(const std::string &type, int numeric, User* user, string_list &results); }; - -#endif diff --git a/make/calcdep.pl b/make/calcdep.pl index 4a759a24a..b0c6941b7 100755 --- a/make/calcdep.pl +++ b/make/calcdep.pl @@ -27,7 +27,7 @@ sub find_output; sub gendep($); sub dep_cpp($$$); sub dep_so($); -sub dep_dir($); +sub dep_dir($$); sub run(); my %f2dep; @@ -44,7 +44,7 @@ sub run() { 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: $!"; @@ -71,20 +71,28 @@ bad-target: \@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'; @@ -96,7 +104,7 @@ END 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"; } @@ -107,7 +115,7 @@ END } my $core_mk = join ' ', @core_deps; - my $cmds = join ' ', @cmdlist; + my $cmods = join ' ', @cmodlist; my $mods = join ' ', @modlist; print MAKE <<END; @@ -116,11 +124,11 @@ bin/inspircd: $core_mk inspircd: bin/inspircd -commands: $cmds +coremods: $cmods modules: $mods -.PHONY: all bad-target inspircd commands modules +.PHONY: all bad-target inspircd coremods modules END } @@ -141,14 +149,14 @@ all: inspircd 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; } @@ -173,11 +181,11 @@ END 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"; @@ -199,7 +207,7 @@ sub gendep($) { 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; @@ -243,8 +251,8 @@ sub dep_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) { @@ -256,7 +264,7 @@ sub dep_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 { diff --git a/make/configure.pm b/make/configure.pm index 606483e98..286424080 100644 --- a/make/configure.pm +++ b/make/configure.pm @@ -21,291 +21,246 @@ # -package make::configure; +BEGIN { + require 5.8.0; +} -require 5.8.0; +package make::configure; use strict; use warnings FATAL => qw(all); +use Cwd; use Exporter 'import'; -use POSIX; + 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 + get_property get_revision + dump_hash); + +my $revision; + +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; +sub cmd_clean { + unlink '.config.cache'; } -sub getrevision { - if ($no_git) - { - return "0"; - } - my $data = `git describe --tags 2>/dev/null`; - if ($data eq "") - { - $no_git = 1; - return '0'; +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 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; } - chomp $data; # remove \n - return $data; + print "Updating...\n"; + %main::config = read_configure_cache(); + %main::cxx = get_compiler_info($main::config{CXX}); + $main::topdir = getcwd(); + main::writefiles(); + print "Update complete!\n"; + exit 0; } -sub getcompilerflags { - my ($file) = @_; - open(FLAGS, $file) or return ""; - while (<FLAGS>) { - if ($_ =~ /^\/\* \$CompileFlags: (.+) \*\/$/) { - my $x = translate_functions($1, $file); - next if ($x eq ""); - close(FLAGS); - return $x; - } +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 ""; + close(CACHE); + return %cfg; } -sub getlinkerflags { - my ($file) = @_; - open(FLAGS, $file) or return ""; - while (<FLAGS>) { - if ($_ =~ /^\/\* \$LinkerFlags: (.+) \*\/$/) { - my $x = translate_functions($1, $file); - next if ($x eq ""); - close(FLAGS); - return $x; - } +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(FLAGS); - return ""; + close(CACHE); + return 1; } -sub getdependencies { - my ($file) = @_; - open(FLAGS, $file) or return ""; - while (<FLAGS>) { - if ($_ =~ /^\/\* \$ModDep: (.+) \*\/$/) { - my $x = translate_functions($1, $file); - next if ($x eq ""); - close(FLAGS); - return $x; - } +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." + ); } - close(FLAGS); - return ""; + return ( + NAME => $binary, + VERSION => '0.0' + ); } -sub nopedantic { - my ($file) = @_; - open(FLAGS, $file) or return ""; - while (<FLAGS>) { - if ($_ =~ /^\/\* \$NoPedantic \*\/$/) { - my $x = translate_functions($_, $file); - next if ($x eq ""); - close(FLAGS); - return 1; +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"; } } - close(FLAGS); - return 0; + return ""; } -sub getmodules -{ - my ($silent) = @_; - - my $i = 0; - - if (!$silent) - { - print "Detecting modules "; - } +sub run_test($$) { + my ($what, $result) = @_; + print "Checking whether $what is available... "; + print $result ? "yes\n" : "no\n"; + return $result; +} - 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 "."; - } - } - } - closedir(DIRHANDLE); +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; +} - if (!$silent) - { - print "\nOk, $i modules.\n"; - } +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 promptnumeric($$) +sub get_property($$;$) { - 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"; + 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 module_installed($) -{ - my $module = shift; - eval("use $module;"); - return !$@; -} - -sub promptstring_s($$) -{ - 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; +sub get_revision { + return $revision if defined $revision; + chomp(my $tags = `git describe --tags 2>/dev/null`); + $revision = $tags || 'release'; + return $revision; } -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"; -} - -sub is_dir -{ - my ($path) = @_; - if (chdir($path)) - { - chdir($main::this); - return 1; - } - else - { - # Just in case.. - chdir($main::this); - return 0; - } -} - -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] - --disable-ipv6 Do not build IPv6 native InspIRCd [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/conf] - --log-dir=[directory] Log file directory for logs - [$PWD/logs] - --data-dir=[directory] Data directory for variable data, such as the permchannel configuration and the XLine database - [$PWD/data] - --module-dir=[directory] Modules directory for loadable modules - [$PWD/modules] - --binary-dir=[directory] Binaries directory for core binary - [$PWD/bin] - --library-dir=[directory] Library directory for core libraries - [$PWD/lib] - --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 - -EOH - exit(0); + 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"; } 1; - diff --git a/make/gnutlscert.pm b/make/gnutlscert.pm deleted file mode 100644 index a67be0cfd..000000000 --- a/make/gnutlscert.pm +++ /dev/null @@ -1,147 +0,0 @@ -# -# 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 $org = promptstring_s("Please enter the organization name", "My IRC Network"); - my $unit = promptstring_s("Please enter the unit Name", "Server Admins"); - my $state = promptstring_s("Please enter your state (two letter code)", "CA"); - my $country = promptstring_s("Please enter your country", "Oompa Loompa Land"); - my $commonname = promptstring_s("Please enter the certificate common name (hostname)", "irc.mynetwork.com"); - my $email = promptstring_s("Please enter a contact email address", "oompa\@loompa.com"); - 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 = - -# 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 = 700 - -# 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; - diff --git a/make/opensslcert.pm b/make/opensslcert.pm deleted file mode 100644 index fd7bd6998..000000000 --- a/make/opensslcert.pm +++ /dev/null @@ -1,61 +0,0 @@ -# -# 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 $org = promptstring_s("Please enter the organization name", "My IRC Network"); - my $unit = promptstring_s("Please enter the unit Name", "Server Admins"); - my $country = promptstring_s("Please enter your country (two letter code)", "US"); - my $state = promptstring_s("Please enter your state or locality name", "Alaska"); - my $city = promptstring_s("Please enter your city", "Factory Town"); - my $email = promptstring_s("Please enter a contact email address", "oompa\@loompa.com"); - my $commonname = promptstring_s("Please enter the common name (domain name) of the irc server", "example.inspircd.org"); - print FH <<__END__; -$country -$state -$city -$org -$unit -$commonname -$email -__END__ -close(FH); - -my $time = promptstring_s("Please enter the number of days that this certificate is valid for","365"); - -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; diff --git a/make/template/inspircd b/make/template/inspircd index 322ee2cd6..c14099323 100644 --- a/make/template/inspircd +++ b/make/template/inspircd @@ -29,11 +29,11 @@ my $binpath = "@BINARY_DIR@"; my $runpath = "@BASE_DIR@"; my $datadir = "@DATA_DIR@"; my $valgrindlogpath = "$basepath/valgrindlogs"; -my $executable = "@EXECUTABLE@"; +my $executable = "inspircd"; my $version = "@VERSION@"; my $uid = "@UID@"; -if ($< == 0 || $> == 0) { +if (!("--runasroot" ~~ @ARGV) && ($< == 0 || $> == 0)) { if ($uid !~ /^\d+$/) { # Named UID, look it up $uid = getpwnam $uid; diff --git a/make/template/main.mk b/make/template/main.mk index e55e36c0a..97e426e0e 100644 --- a/make/template/main.mk +++ b/make/template/main.mk @@ -30,13 +30,13 @@ # -CC = @CC@ +CXX = @CXX@ +COMPILER = @COMPILER@ SYSTEM = @SYSTEM@ BUILDPATH = @BUILD_DIR@ 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@" @@ -46,16 +46,21 @@ DATPATH = "$(DESTDIR)@DATA_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) icc - 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 @@ -70,19 +75,11 @@ INSTMODE_LIB = 0644 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 @@ -90,48 +87,50 @@ INSTMODE_LIB = 0644 DBGOK=0 @IFEQ $(D) 0 - CXXFLAGS += -O2 -@IFEQ $(CC) gcc - CXXFLAGS += -g1 + CORECXXFLAGS += -O2 +@IFEQ $(CXX) g++ + 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 - @GNU_ONLY MAKEFLAGS += --no-print-directory @GNU_ONLY SOURCEPATH = $(shell /bin/pwd) @BSD_ONLY 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) + 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 +# 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 SPLIT_CC # Default target @@ -230,6 +229,7 @@ install: target [ $(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_BIN) tools/genssl $(BINPATH)/inspircd-genssl 2>/dev/null -$(INSTALL) -m $(INSTMODE_LIB) tools/gdbargs $(BASE)/.gdbargs 2>/dev/null -$(INSTALL) -m $(INSTMODE_LIB) docs/conf/*.example $(CONPATH)/examples -$(INSTALL) -m $(INSTMODE_LIB) docs/conf/aliases/*.example $(CONPATH)/examples/aliases @@ -248,9 +248,7 @@ install: target @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 @@ -269,14 +267,11 @@ deinstall: -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 @@ -312,4 +307,4 @@ help: @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 diff --git a/include/modes/umode_o.h b/make/test/clock_gettime.cpp index f9644a097..91d8cd412 100644 --- a/include/modes/umode_o.h +++ b/make/test/clock_gettime.cpp @@ -1,7 +1,6 @@ /* * 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 @@ -17,15 +16,10 @@ */ -#include "mode.h" +#include <time.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); -}; +int main() { + timespec time_spec; + clock_gettime(CLOCK_REALTIME, &time_spec); + return 0; +} diff --git a/make/check_eventfd.cpp b/make/test/eventfd.cpp index 980d04485..980d04485 100644 --- a/make/check_eventfd.cpp +++ b/make/test/eventfd.cpp diff --git a/make/check_epoll.cpp b/make/test/kqueue.cpp index 918d3907e..a8b9882cf 100644 --- a/make/check_epoll.cpp +++ b/make/test/kqueue.cpp @@ -16,9 +16,10 @@ */ -#include <sys/epoll.h> +#include <sys/types.h> +#include <sys/event.h> int main() { - int fd = epoll_create(1); + int fd = kqueue(); return (fd < 0); } diff --git a/make/unit-cc.pl b/make/unit-cc.pl index 7474365aa..77f97a3f2 100755 --- a/make/unit-cc.pl +++ b/make/unit-cc.pl @@ -52,7 +52,7 @@ exit 1; 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; @@ -97,12 +97,10 @@ sub do_compile { 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}; @@ -110,7 +108,7 @@ sub do_compile { if ($do_link) { $flags = join ' ', $flags, $ENV{PICLDFLAGS}; - $libs = join ' ', getlinkerflags($file); + $libs = get_property($file, 'LinkerFlags'); } else { $flags .= ' -c'; } diff --git a/make/utilities.pm b/make/utilities.pm index ebca57b68..9d791870a 100644 --- a/make/utilities.pm +++ b/make/utilities.pm @@ -20,18 +20,23 @@ # -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); +use File::Path; +use File::Spec::Functions qw(rel2abs); +use Getopt::Long; +use POSIX; + +our @EXPORT = qw(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); # Parse the output of a *_config program, # such as pcre_config, take out the -L @@ -42,6 +47,62 @@ our @EXPORT = qw(make_rpath pkgconfig_get_include_dirs pkgconfig_get_lib_dirs pk my %already_added = (); my $if_skip_lines = 0; +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($$$$$) { my ($prompt, $configitem, $default, $package, $commandlineswitch) = @_; @@ -108,15 +169,6 @@ sub pkgconfig_get_include_dirs($$$;$) { 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... "; @@ -224,15 +276,6 @@ sub pkgconfig_get_lib_dirs($$$;$) { 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... "; @@ -307,16 +350,6 @@ sub translate_functions($$) $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 =~ /ifuname\(\!"(\w+)"\)/) { my $uname = $1; diff --git a/modulemanager b/modulemanager index 24f4467a4..86f9ca0c4 100755 --- a/modulemanager +++ b/modulemanager @@ -22,7 +22,7 @@ use strict; use warnings FATAL => qw(all); -use make::configure; +use make::utilities; BEGIN { unless (module_installed("LWP::Simple")) { @@ -31,12 +31,12 @@ BEGIN { 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 @@ -131,8 +131,6 @@ while (<SRC>) { } close SRC; -getmodules(1); - # determine core version `./src/version.sh` =~ /InspIRCd-([0-9.]+)/ or die "Cannot determine inspircd version"; $installed{core} = $1; @@ -156,9 +154,8 @@ $modules{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; diff --git a/src/bancache.cpp b/src/bancache.cpp index 52449e55e..4bb2fa82c 100644 --- a/src/bancache.cpp +++ b/src/bancache.cpp @@ -18,34 +18,16 @@ */ -/* $Core */ - #include "inspircd.h" #include "bancache.h" -BanCacheHit *BanCacheManager::AddHit(const std::string &ip, const std::string &type, const std::string &reason) -{ - 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; } @@ -55,113 +37,60 @@ BanCacheHit *BanCacheManager::GetHit(const std::string &ip) 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. + if (ServerInstance->Time() < it->second->Expiry) + return false; - 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); - } - - 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() diff --git a/src/base.cpp b/src/base.cpp index 66a3cb140..dc57a8434 100644 --- a/src/base.cpp +++ b/src/base.cpp @@ -28,13 +28,13 @@ classbase::classbase() { if (ServerInstance && ServerInstance->Logs) - ServerInstance->Logs->Log("CULLLIST", DEBUG, "classbase::+ @%p", (void*)this); + 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", + ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "classbase::-%s @%p", typeid(*this).name(), (void*)this); return CullResult(); } @@ -42,7 +42,7 @@ CullResult classbase::cull() classbase::~classbase() { if (ServerInstance && ServerInstance->Logs) - ServerInstance->Logs->Log("CULLLIST", DEBUG, "classbase::~ @%p", (void*)this); + ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "classbase::~ @%p", (void*)this); } CullResult::CullResult() @@ -74,14 +74,14 @@ refcountbase::refcountbase() : refcount(0) refcountbase::~refcountbase() { if (refcount && ServerInstance && ServerInstance->Logs) - ServerInstance->Logs->Log("CULLLIST", DEBUG, "refcountbase::~ @%p with refcount %d", + 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", + ServerInstance->Logs->Log("CULLLIST", LOG_DEBUG, "usecountbase::~ @%p with refcount %d", (void*)this, usecount); } @@ -174,32 +174,31 @@ void Extensible::doUnhookExtensions(const std::vector<reference<ExtensionItem> > } } -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) + 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) @@ -312,4 +311,3 @@ ModuleException::ModuleException(const std::string &message, Module* who) : CoreException(message, who ? who->ModuleSourceFile : "A Module") { } - diff --git a/src/channels.cpp b/src/channels.cpp index 6c9bd7c85..4555deb96 100644 --- a/src/channels.cpp +++ b/src/channels.cpp @@ -23,154 +23,92 @@ */ -/* $Core */ - #include "inspircd.h" +#include "listmode.h" #include <cstdarg> #include "mode.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; + modes[mh->GetId()] = on; } -void Channel::SetModeParam(char mode, const std::string& parameter) +void Channel::SetTopic(User* u, const std::string& ntopic) { - 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 ""; -} - -std::string Channel::GetModeParameter(ModeHandler* mode) -{ - 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; + Membership*& memb = userlist[user]; + if (memb) + return NULL; + + memb = new Membership(user, this); return memb; } void Channel::DelUser(User* user) { - UserMembIter a = userlist.find(user); + UserMembIter 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 UserMembIter& membiter) { - return (userlist.find(user) != userlist.end()); + Membership* memb = membiter->second; + memb->cull(); + delete memb; + userlist.erase(membiter); + + // If this channel became empty then it should be removed + CheckDestroy(); } Membership* Channel::GetUser(User* user) @@ -181,14 +119,9 @@ Membership* Channel::GetUser(User* user) 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; @@ -201,6 +134,9 @@ void Channel::SetDefaultModes() ModeHandler* mode = ServerInstance->Modes->FindMode(*n, MODETYPE_CHANNEL); if (mode) { + if (mode->IsPrefixMode()) + continue; + if (mode->GetNumParams(true)) list.GetToken(parameter); else @@ -215,14 +151,13 @@ void Channel::SetDefaultModes() * 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. @@ -230,13 +165,13 @@ Channel* Channel::JoinUser(User *user, const char* cn, bool override, const char * We restrict local operators to OperMaxChans 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")) { if (user->chans.size() >= ServerInstance->Config->OperMaxChans) { - user->WriteNumeric(ERR_TOOMANYCHANNELS, "%s %s :You are on too many channels",user->nick.c_str(), cn); + user->WriteNumeric(ERR_TOOMANYCHANNELS, "%s :You are on too many channels", cname.c_str()); return NULL; } } @@ -247,102 +182,99 @@ Channel* Channel::JoinUser(User *user, const char* cn, bool override, const char 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); + 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); + + 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 (!Ptr) + 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; } @@ -352,67 +284,76 @@ Channel* Channel::JoinUser(User *user, const char* cn, bool override, const char */ 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) +void 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; + } + + Membership* memb = this->AddUser(user); + if (!memb) + return; // Already on the channel - Membership* memb = Ptr->AddUser(user); - user->chans.insert(Ptr); + user->chans.push_front(memb); - for (std::string::const_iterator x = privs.begin(); x != privs.end(); x++) + 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)); } bool Channel::IsBanned(User* user) @@ -423,10 +364,15 @@ 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; } @@ -446,10 +392,9 @@ bool Channel::CheckBan(User* user, const std::string& mask) 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) || @@ -466,12 +411,14 @@ ModResult Channel::GetExtBanStatus(User *user, char type) 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; } } @@ -479,61 +426,54 @@ ModResult Channel::GetExtBanStatus(User *user, char type) } /* 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; - - Membership* memb = GetUser(user); + UserMembIter membiter = userlist.find(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, User* victim, const std::string& reason, Membership* srcmemb) { - if (!src || !user || !reason) + UserMembIter victimiter = userlist.find(victim); + Membership* memb = ((victimiter != userlist.end()) ? victimiter->second : NULL); + + if (!memb) + { + src->WriteNumeric(ERR_USERNOTINCHANNEL, "%s %s :They are not on that channel", victim->nick.c_str(), this->name.c_str()); return; + } - Membership* memb = GetUser(user); + // Do the following checks only if the KICK is done by a local user; + // each server enforces its own rules. 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; - } - + // Modules are allowed to explicitly allow or deny kicks done by local users ModResult res; - if (ServerInstance->ULine(src->server)) - res = MOD_RES_ALLOW; - else - FIRST_MOD_RESULT(OnUserPreKick, res, (src,memb,reason)); - + 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); + if (!srcmemb) + srcmemb = GetUser(src); + unsigned int them = srcmemb ? srcmemb->getRank() : 0; unsigned int req = HALFOP_VALUE; for (std::string::size_type i = 0; i < memb->modes.length(); i++) { @@ -544,85 +484,55 @@ void Channel::KickUser(User *src, User *user, const char* reason) 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-"); + src->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You must be a channel %soperator", + 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); + CUList except_list; + FOREACH_MOD(OnUserKick, (src, memb, reason, except_list)); - user->chans.erase(this); - this->RemoveAllPrefixes(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++) { 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]; - - snprintf(tb,MAXBUF,":%s %s", ServName.empty() ? ServerInstance->Config->ServerName.c_str() : ServName.c_str(), text.c_str()); - std::string out = tb; + const std::string message = ":" + (ServName.empty() ? ServerInstance->Config->ServerName : ServName) + " " + text; for (UserMembIter i = userlist.begin(); i != userlist.end(); i++) { if (IS_LOCAL(i->first)) - i->first->Write(out); + i->first->Write(message); } } @@ -630,43 +540,23 @@ void Channel::WriteChannelWithServ(const std::string& ServName, const std::strin * 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) @@ -674,7 +564,7 @@ void Channel::RawWriteAllExcept(User* user, bool serversource, char status, CULi unsigned int minrank = 0; if (status) { - ModeHandler* mh = ServerInstance->Modes->FindPrefix(status); + PrefixMode* mh = ServerInstance->Modes->FindPrefix(status); if (mh) minrank = mh->GetPrefixRank(); } @@ -698,59 +588,39 @@ void Channel::WriteAllExceptSender(User* user, bool serversource, char status, c 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() +const char* Channel::ChanModes(bool showkey) { - int count = 0; - for (UserMembIter i = userlist.begin(); i != userlist.end(); i++) - { - if (!i->first->quitting && !i->first->IsModeSet('i')) - count++; - } + static std::string scratch; + std::string sparam; - return count; -} - -char* Channel::ChanModes(bool showkey) -{ - static char scratch[MAXBUF]; - static char sparam[MAXBUF]; - char* offset = scratch; - std::string extparam; - - *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 @@ -758,20 +628,15 @@ char* Channel::ChanModes(bool showkey) */ void Channel::UserList(User *user) { - if (!IS_LOCAL(user)) - return; - bool has_privs = user->HasPrivPermission("channels/auspex"); - - if (this->IsModeSet('s') && !this->HasUser(user) && !has_privs) + if (this->IsModeSet(secretmode) && !this->HasUser(user) && !has_privs) { - user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(), this->name.c_str()); + user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", 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(); @@ -783,14 +648,12 @@ void Channel::UserList(User *user) */ 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) { - 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 @@ -799,10 +662,15 @@ void Channel::UserList(User *user) 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)); + FOREACH_MOD(OnNamesListItem, (user, memb, prefixlist, nick)); /* Nick was nuked, a module wants us to skip it */ if (nick.empty()) @@ -829,57 +697,25 @@ void Channel::UserList(User *user) 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; @@ -891,28 +727,23 @@ unsigned int Membership::getRank() 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; @@ -927,48 +758,33 @@ unsigned int Channel::GetPrefixValue(User* user) 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) @@ -976,37 +792,34 @@ void Invitation::Create(Channel* c, LocalUser* u, time_t timeout) 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); + 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); if (!u || u->invites.empty()) return NULL; - InviteList locallist; - locallist.swap(u->invites); - 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 @@ -1017,34 +830,17 @@ Invitation* Invitation::Find(Channel* c, LocalUser* u, bool check_expired) 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); } diff --git a/src/cidr.cpp b/src/cidr.cpp index 333875665..875b95304 100644 --- a/src/cidr.cpp +++ b/src/cidr.cpp @@ -19,8 +19,6 @@ */ -/* $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 @@ -82,5 +80,3 @@ bool irc::sockets::MatchCIDR(const std::string &address, const std::string &cidr return mask == mask2; } - - diff --git a/src/command_parse.cpp b/src/command_parse.cpp index 1c72c7de4..d89d7cbb5 100644 --- a/src/command_parse.cpp +++ b/src/command_parse.cpp @@ -23,112 +23,88 @@ #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 (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; - - if (extra >= (signed)parameters.size()) - extra = -1; + return false; - /* 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) @@ -142,7 +118,7 @@ Command* CommandParser::GetHandler(const std::string &commandname) // 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); @@ -174,6 +150,8 @@ CmdResult CommandParser::CallHandler(const std::string &commandname, const std:: if (bOkay) { + if (cmd) + *cmd = n->second; return n->second->Handle(parameters,user); } } @@ -181,7 +159,7 @@ CmdResult CommandParser::CallHandler(const std::string &commandname, const std:: 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); @@ -196,29 +174,27 @@ bool CommandParser::ProcessCommand(LocalUser *user, std::string &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. @@ -227,17 +203,19 @@ bool CommandParser::ProcessCommand(LocalUser *user, std::string &cmd) * 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()); + user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s :Unknown command",command.c_str()); ServerInstance->stats->statsUnknown++; - return true; + 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): @@ -246,32 +224,21 @@ bool CommandParser::ProcessCommand(LocalUser *user, std::string &cmd) * 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()); } /* @@ -281,76 +248,75 @@ bool CommandParser::ProcessCommand(LocalUser *user, std::string &cmd) 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)); } } @@ -361,19 +327,23 @@ void CommandParser::RemoveCommand(Command* x) cmdlist.erase(n); } +CommandBase::~CommandBase() +{ +} + Command::~Command() { 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) @@ -391,88 +361,64 @@ CommandParser::CommandParser() { } -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; } diff --git a/src/commands.cpp b/src/commands.cpp index d805a1f65..d5a89c086 100644 --- a/src/commands.cpp +++ b/src/commands.cpp @@ -21,104 +21,10 @@ */ -/* $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) { if (IS_LOCAL(u)) @@ -127,7 +33,7 @@ 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; } @@ -145,4 +51,3 @@ CmdResult SplitCommand::HandleServer(const std::vector<std::string>&, FakeUser*) { return CMD_INVALID; } - diff --git a/src/commands/cmd_clearcache.cpp b/src/commands/cmd_clearcache.cpp deleted file mode 100644 index 5914f9a8f..000000000 --- a/src/commands/cmd_clearcache.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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) diff --git a/src/commands/cmd_connect.cpp b/src/commands/cmd_connect.cpp deleted file mode 100644 index 7cf4752d2..000000000 --- a/src/commands/cmd_connect.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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> [<remote-server>]"; } - /** 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) diff --git a/src/commands/cmd_join.cpp b/src/commands/cmd_join.cpp deleted file mode 100644 index 6124fcc1c..000000000 --- a/src/commands/cmd_join.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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) diff --git a/src/commands/cmd_links.cpp b/src/commands/cmd_links.cpp deleted file mode 100644 index f4152ebc5..000000000 --- a/src/commands/cmd_links.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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) diff --git a/src/commands/cmd_loadmodule.cpp b/src/commands/cmd_loadmodule.cpp deleted file mode 100644 index 379e597e4..000000000 --- a/src/commands/cmd_loadmodule.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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) diff --git a/src/commands/cmd_map.cpp b/src/commands/cmd_map.cpp deleted file mode 100644 index 385a2c752..000000000 --- a/src/commands/cmd_map.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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) diff --git a/src/commands/cmd_mode.cpp b/src/commands/cmd_mode.cpp deleted file mode 100644 index 17e21b182..000000000 --- a/src/commands/cmd_mode.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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) diff --git a/src/commands/cmd_notice.cpp b/src/commands/cmd_notice.cpp deleted file mode 100644 index d5ef7ba1d..000000000 --- a/src/commands/cmd_notice.cpp +++ /dev/null @@ -1,229 +0,0 @@ -/* - * 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) diff --git a/src/commands/cmd_part.cpp b/src/commands/cmd_part.cpp deleted file mode 100644 index aadb42d90..000000000 --- a/src/commands/cmd_part.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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) diff --git a/src/commands/cmd_pass.cpp b/src/commands/cmd_pass.cpp deleted file mode 100644 index f082a4ce8..000000000 --- a/src/commands/cmd_pass.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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) diff --git a/src/commands/cmd_ping.cpp b/src/commands/cmd_ping.cpp deleted file mode 100644 index 3ccadd929..000000000 --- a/src/commands/cmd_ping.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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) diff --git a/src/commands/cmd_pong.cpp b/src/commands/cmd_pong.cpp deleted file mode 100644 index 825168de7..000000000 --- a/src/commands/cmd_pong.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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) diff --git a/src/commands/cmd_privmsg.cpp b/src/commands/cmd_privmsg.cpp deleted file mode 100644 index cefdd4800..000000000 --- a/src/commands/cmd_privmsg.cpp +++ /dev/null @@ -1,237 +0,0 @@ -/* - * 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) diff --git a/src/commands/cmd_quit.cpp b/src/commands/cmd_quit.cpp deleted file mode 100644 index 6a6b447e5..000000000 --- a/src/commands/cmd_quit.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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) diff --git a/src/commands/cmd_rules.cpp b/src/commands/cmd_rules.cpp deleted file mode 100644 index 5d41aa4b8..000000000 --- a/src/commands/cmd_rules.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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 = NULL; - 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) diff --git a/src/commands/cmd_server.cpp b/src/commands/cmd_server.cpp deleted file mode 100644 index d54eca392..000000000 --- a/src/commands/cmd_server.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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) diff --git a/src/commands/cmd_squit.cpp b/src/commands/cmd_squit.cpp deleted file mode 100644 index 064d2a31d..000000000 --- a/src/commands/cmd_squit.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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> [<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); -}; - - -/* - * 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) diff --git a/src/commands/cmd_time.cpp b/src/commands/cmd_time.cpp deleted file mode 100644 index db452d381..000000000 --- a/src/commands/cmd_time.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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) diff --git a/src/commands/cmd_topic.cpp b/src/commands/cmd_topic.cpp deleted file mode 100644 index 412ca1c06..000000000 --- a/src/commands/cmd_topic.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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) diff --git a/src/commands/cmd_unloadmodule.cpp b/src/commands/cmd_unloadmodule.cpp deleted file mode 100644 index 6d0f5f41c..000000000 --- a/src/commands/cmd_unloadmodule.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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) diff --git a/src/commands/cmd_version.cpp b/src/commands/cmd_version.cpp deleted file mode 100644 index 7620197fd..000000000 --- a/src/commands/cmd_version.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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) diff --git a/src/commands/cmd_whois.cpp b/src/commands/cmd_whois.cpp deleted file mode 100644 index ba2ad9c15..000000000 --- a/src/commands/cmd_whois.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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 = abs((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) diff --git a/src/commands/cmd_whowas.cpp b/src/commands/cmd_whowas.cpp deleted file mode 100644 index 3a6444b45..000000000 --- a/src/commands/cmd_whowas.cpp +++ /dev/null @@ -1,348 +0,0 @@ -/* - * 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) diff --git a/src/configparser.cpp b/src/configparser.cpp index 825dfc966..92f05edd4 100644 --- a/src/configparser.cpp +++ b/src/configparser.cpp @@ -119,7 +119,7 @@ struct Parser while (1) { ch = next(); - if (ch == '&' && (flags & FLAG_USE_XML)) + if (ch == '&' && !(flags & FLAG_USE_COMPAT)) { std::string varname; while (1) @@ -141,7 +141,7 @@ struct Parser 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') @@ -183,7 +183,10 @@ struct Parser 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) { @@ -211,7 +214,7 @@ struct Parser } 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"); @@ -223,9 +226,9 @@ struct Parser { 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); } @@ -297,7 +300,7 @@ void ParseStack::DoInclude(ConfigTag* tag, int flags) 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)) @@ -320,14 +323,15 @@ void ParseStack::DoReadFile(const std::string& key, const std::string& name, int 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); @@ -340,25 +344,25 @@ void ParseStack::DoReadFile(const std::string& key, const std::string& name, int } } -bool ParseStack::ParseFile(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) { - ServerInstance->Logs->Log("CONFIG", DEBUG, "Reading file %s", name.c_str()); + ServerInstance->Logs->Log("CONFIG", LOG_DEBUG, "Reading file %s", path.c_str()); for (unsigned int t = 0; t < reading.size(); t++) { - if (std::string(name) == reading[t]) + if (path == reading[t]) { - throw CoreException("File " + name + " is included recursively (looped inclusion)"); + throw CoreException("File " + path + " 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")); + FileWrapper file(fopen(path.c_str(), "r")); if (!file) - throw CoreException("Could not read \"" + 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; @@ -366,7 +370,7 @@ bool ParseStack::ParseFile(const std::string& name, int flags, const std::string bool ParseStack::ParseExec(const std::string& name, int flags, const std::string& mandatory_tag) { - ServerInstance->Logs->Log("CONFIG", DEBUG, "Reading executable %s", name.c_str()); + ServerInstance->Logs->Log("CONFIG", LOG_DEBUG, "Reading executable %s", name.c_str()); for (unsigned int t = 0; t < reading.size(); t++) { if (std::string(name) == reading[t]) @@ -399,7 +403,7 @@ bool ConfigTag::readString(const std::string& key, std::string& value, bool allo 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') @@ -417,7 +421,7 @@ std::string ConfigTag::getString(const std::string& key, const std::string& def) 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)) @@ -431,18 +435,41 @@ long ConfigTag::getInt(const std::string &key, long def) 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; @@ -462,7 +489,7 @@ bool ConfigTag::getBool(const std::string &key, bool def) 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; } diff --git a/src/configreader.cpp b/src/configreader.cpp index 060f66d16..cda5e03e0 100644 --- a/src/configreader.cpp +++ b/src/configreader.cpp @@ -23,26 +23,20 @@ #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(); + SoftLimit = SocketEngine::GetMaxFds(); MaxConn = SOMAXCONN; MaxChans = 20; OperMaxChans = 30; @@ -50,60 +44,6 @@ ServerConfig::ServerConfig() c_ipv6_range = 128; } -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; @@ -145,64 +85,6 @@ bool ServerConfig::ApplyDisabledCommands(const std::string& data) 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); @@ -241,13 +123,11 @@ void ServerConfig::CrossCheckOperClassType() 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; @@ -272,8 +152,8 @@ void ServerConfig::CrossCheckOperClassType() 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()); @@ -419,6 +299,7 @@ void ServerConfig::CrossCheckConnectBlocks(ServerConfig* current) 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()) @@ -436,42 +317,31 @@ void ServerConfig::CrossCheckConnectBlocks(ServerConfig* current) /** 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() @@ -480,62 +350,47 @@ 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(), 10, SocketEngine::GetMaxFds()); + 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); c_ipv4_range = ConfValue("cidr")->getInt("ipv4clone", 32); @@ -544,25 +399,22 @@ void ServerConfig::Fill() 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", CONFIG_PATH); + Paths.Data = ConfValue("path")->getString("datadir", DATA_PATH); + Paths.Log = ConfValue("path")->getString("logdir", LOG_PATH); + Paths.Module = ConfValue("path")->getString("moduledir", MOD_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") @@ -580,26 +432,7 @@ void ServerConfig::Fill() 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")); @@ -665,12 +498,7 @@ void ServerConfig::Read() catch (CoreException& err) { valid = false; - errstr << err.GetReason(); - } - if (valid) - { - DNSServer = ConfValue("dns")->getString("server"); - FindDNS(DNSServer); + errstr << err.GetReason() << std::endl; } } @@ -690,16 +518,26 @@ void ServerConfig::Apply(ServerConfig* old, const std::string &useruid) /* 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; + } } } @@ -721,6 +559,11 @@ void ServerConfig::Apply(ServerConfig* old, const std::string &useruid) 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 @@ -728,14 +571,14 @@ void ServerConfig::Apply(ServerConfig* old, const std::string &useruid) 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; } } } @@ -744,7 +587,7 @@ void ServerConfig::Apply(ServerConfig* old, const std::string &useruid) 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(); } @@ -778,10 +621,6 @@ void ServerConfig::Apply(ServerConfig* old, const std::string &useruid) 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 */ @@ -810,13 +649,8 @@ void ServerConfig::Apply(ServerConfig* old, const std::string &useruid) 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) @@ -832,30 +666,27 @@ void ServerConfig::ApplyModules(User* user) } } - 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()); } } @@ -865,25 +696,20 @@ void ServerConfig::ApplyModules(User* user) { 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); @@ -892,7 +718,7 @@ ConfigTag* ServerConfig::ConfValue(const std::string &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; } @@ -902,35 +728,28 @@ ConfigTagList ServerConfig::ConfTags(const std::string& tag) 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() @@ -942,7 +761,7 @@ 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); @@ -956,12 +775,17 @@ void ConfigReaderThread::Finish() */ 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); + + ServerInstance->ISupport.Build(); ServerInstance->Logs->CloseLogs(); ServerInstance->Logs->OpenFileLogs(); diff --git a/src/commands/cmd_invite.cpp b/src/coremods/core_channel/cmd_invite.cpp index c69e6bd1b..3260d7862 100644 --- a/src/commands/cmd_invite.cpp +++ b/src/coremods/core_channel/cmd_invite.cpp @@ -21,26 +21,14 @@ #include "inspircd.h" +#include "core_channel.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 +CommandInvite::CommandInvite(Module* parent) + : Command(parent, "INVITE", 0, 0) { - 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); -}; + Penalty = 4; + syntax = "[<nick> <channel>]"; +} /** Handle /INVITE */ @@ -61,26 +49,26 @@ CmdResult CommandInvite::Handle (const std::vector<std::string>& parameters, Use if (parameters.size() == 3) { if (IS_LOCAL(user)) - timeout = ServerInstance->Time() + ServerInstance->Duration(parameters[2]); + timeout = ServerInstance->Time() + InspIRCd::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()); + user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", 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()); + 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 %s :is already on channel",user->nick.c_str(),u->nick.c_str(),c->name.c_str()); + user->WriteNumeric(ERR_USERONCHANNEL, "%s %s :is already on channel", u->nick.c_str(), c->name.c_str()); return CMD_FAILURE; } @@ -99,17 +87,22 @@ CmdResult CommandInvite::Handle (const std::vector<std::string>& parameters, Use { // 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-" : "")); + 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)) - 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()); + { + 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; @@ -122,7 +115,7 @@ CmdResult CommandInvite::Handle (const std::vector<std::string>& parameters, Use } case ServerConfig::INVITE_ANNOUNCE_DYNAMIC: { - ModeHandler* mh = ServerInstance->Modes->FindMode('h', MODETYPE_CHANNEL); + PrefixMode* mh = ServerInstance->Modes->FindPrefixMode('h'); prefix = (mh && mh->name == "halfop" ? mh->GetPrefix() : '@'); break; } @@ -134,7 +127,7 @@ CmdResult CommandInvite::Handle (const std::vector<std::string>& parameters, Use } 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)); + FOREACH_MOD(OnUserInvite, (user,u,c,timeout)); } else if (IS_LOCAL(user)) { @@ -143,12 +136,14 @@ CmdResult CommandInvite::Handle (const std::vector<std::string>& parameters, Use 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_INVITELIST, ":%s", (*i)->chan->name.c_str()); } - user->WriteNumeric(RPL_ENDOFINVITELIST, "%s :End of INVITE list",user->nick.c_str()); + user->WriteNumeric(RPL_ENDOFINVITELIST, ":End of INVITE list"); } return CMD_SUCCESS; } - -COMMAND_INIT(CommandInvite) +RouteDescriptor CommandInvite::GetRouting(User* user, const std::vector<std::string>& parameters) +{ + return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST); +} diff --git a/src/coremods/core_channel/cmd_join.cpp b/src/coremods/core_channel/cmd_join.cpp new file mode 100644 index 000000000..1945bf52e --- /dev/null +++ b/src/coremods/core_channel/cmd_join.cpp @@ -0,0 +1,60 @@ +/* + * 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; +} diff --git a/src/commands/cmd_kick.cpp b/src/coremods/core_channel/cmd_kick.cpp index c497dd459..715f35d54 100644 --- a/src/commands/cmd_kick.cpp +++ b/src/coremods/core_channel/cmd_kick.cpp @@ -19,26 +19,13 @@ #include "inspircd.h" +#include "core_channel.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 +CommandKick::CommandKick(Module* parent) + : Command(parent, "KICK", 2, 3) { - 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); -}; + syntax = "<channel> <nick>{,<nick>} [<reason>]"; +} /** Handle /KICK */ @@ -48,7 +35,7 @@ CmdResult CommandKick::Handle (const std::vector<std::string>& parameters, User Channel* c = ServerInstance->FindChan(parameters[0]); User* u; - if (ServerInstance->Parser->LoopCall(user, this, parameters, 1)) + if (CommandParser::LoopCall(user, this, parameters, 1)) return CMD_SUCCESS; if (IS_LOCAL(user)) @@ -58,14 +45,25 @@ CmdResult CommandKick::Handle (const std::vector<std::string>& parameters, User 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()); + user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", c ? parameters[1].c_str() : parameters[0].c_str()); return CMD_FAILURE; } - if ((IS_LOCAL(user)) && (!c->HasUser(user)) && (!ServerInstance->ULine(user->server))) + Membership* srcmemb = NULL; + if (IS_LOCAL(user)) { - user->WriteServ( "442 %s %s :You're not on that channel!", user->nick.c_str(), parameters[0].c_str()); - return CMD_FAILURE; + 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; + } } if (parameters.size() > 2) @@ -77,9 +75,12 @@ CmdResult CommandKick::Handle (const std::vector<std::string>& parameters, User reason.assign(user->nick, 0, ServerInstance->Config->Limits.MaxKick); } - c->KickUser(user, u, reason.c_str()); + c->KickUser(user, u, reason, srcmemb); return CMD_SUCCESS; } -COMMAND_INIT(CommandKick) +RouteDescriptor CommandKick::GetRouting(User* user, const std::vector<std::string>& parameters) +{ + return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST); +} diff --git a/src/commands/cmd_names.cpp b/src/coremods/core_channel/cmd_names.cpp index fd4a9cef5..13d912376 100644 --- a/src/commands/cmd_names.cpp +++ b/src/coremods/core_channel/cmd_names.cpp @@ -19,26 +19,14 @@ #include "inspircd.h" +#include "core_channel.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 +CommandNames::CommandNames(Module* parent) + : Command(parent, "NAMES", 0, 0) + , secretmode(parent, "secret") { - 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); -}; + syntax = "{<channel>{,<channel>}}"; +} /** Handle /NAMES */ @@ -48,11 +36,11 @@ CmdResult CommandNames::Handle (const std::vector<std::string>& parameters, User if (!parameters.size()) { - user->WriteNumeric(366, "%s * :End of /NAMES list.",user->nick.c_str()); + user->WriteNumeric(RPL_ENDOFNAMES, "* :End of /NAMES list."); return CMD_SUCCESS; } - if (ServerInstance->Parser->LoopCall(user, this, parameters, 0)) + if (CommandParser::LoopCall(user, this, parameters, 0)) return CMD_SUCCESS; c = ServerInstance->FindChan(parameters[0]); @@ -62,10 +50,8 @@ CmdResult CommandNames::Handle (const std::vector<std::string>& parameters, User } 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_SUCCESS; } - -COMMAND_INIT(CommandNames) diff --git a/src/coremods/core_channel/cmd_topic.cpp b/src/coremods/core_channel/cmd_topic.cpp new file mode 100644 index 000000000..ea723c024 --- /dev/null +++ b/src/coremods/core_channel/cmd_topic.cpp @@ -0,0 +1,86 @@ +/* + * 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; +} diff --git a/include/modes/cmode_o.h b/src/coremods/core_channel/core_channel.cpp index c5f1764c1..47f722e1e 100644 --- a/include/modes/cmode_o.h +++ b/src/coremods/core_channel/core_channel.cpp @@ -1,7 +1,7 @@ /* * InspIRCd -- Internet Relay Chat Daemon * - * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org> + * 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 @@ -17,21 +17,27 @@ */ -#include "mode.h" -#include "channels.h" +#include "inspircd.h" +#include "core_channel.h" -class InspIRCd; - -/** Channel mode +o - */ -class ModeChannelOp : public ModeHandler +class CoreModChannel : public Module { - private: + CommandInvite cmdinvite; + CommandJoin cmdjoin; + CommandKick cmdkick; + CommandNames cmdnames; + CommandTopic cmdtopic; + 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); + 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) diff --git a/src/coremods/core_channel/core_channel.h b/src/coremods/core_channel/core_channel.h new file mode 100644 index 000000000..d3adbc9c9 --- /dev/null +++ b/src/coremods/core_channel/core_channel.h @@ -0,0 +1,114 @@ +/* + * 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); +}; diff --git a/src/coremods/core_dns.cpp b/src/coremods/core_dns.cpp new file mode 100644 index 000000000..30c736757 --- /dev/null +++ b/src/coremods/core_dns.cpp @@ -0,0 +1,833 @@ +/* + * 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("RESOLVER", LOG_DEBUG, "Resolver: 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("RESOLVER", LOG_DEBUG, "Resolver: 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("RESOLVER", LOG_DEBUG, "Resolver: " + 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("RESOLVER", LOG_DEBUG, "Resolver: 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("RESOLVER", LOG_SPARSE, "Resolver: 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("RESOLVER", LOG_DEBUG, "Resolver: 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("RESOLVER", LOG_DEBUG, "Resolver: 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, ServerInstance->Time(), 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("RESOLVER", LOG_DEBUG, "Resolver: 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("RESOLVER", LOG_DEBUG, "Resolver: 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("RESOLVER", LOG_DEBUG, "Resolver: 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("RESOLVER", LOG_DEBUG, "Resolver: 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("RESOLVER", LOG_DEBUG, "Resolver: Received an answer for something we didn't request"); + return; + } + + if (recv_packet.flags & QUERYFLAGS_OPCODE) + { + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: Received a nonstandard query"); + ServerInstance->stats->statsDnsBad++; + 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("RESOLVER", LOG_DEBUG, "Resolver: format error"); + error = ERROR_FORMAT_ERROR; + break; + case 2: + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: server error"); + error = ERROR_SERVER_FAILURE; + break; + case 3: + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: domain not found"); + error = ERROR_DOMAIN_NOT_FOUND; + break; + case 4: + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: not implemented"); + error = ERROR_NOT_IMPLEMENTED; + break; + case 5: + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: refused"); + error = ERROR_REFUSED; + break; + default: + break; + } + + ServerInstance->stats->statsDnsBad++; + recv_packet.error = error; + request->OnError(&recv_packet); + } + else if (recv_packet.questions.empty() || recv_packet.answers.empty()) + { + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: No resource records returned"); + ServerInstance->stats->statsDnsBad++; + recv_packet.error = ERROR_NO_RECORDS; + request->OnError(&recv_packet); + } + else + { + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: Lookup complete for " + request->name); + ServerInstance->stats->statsDnsGood++; + request->OnLookupComplete(&recv_packet); + this->AddCache(recv_packet); + } + + ServerInstance->stats->statsDns++; + + /* Request's destructor removes it from the request map */ + delete request; + } + + bool Tick(time_t now) + { + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: 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("RESOLVER", LOG_SPARSE, "Resolver: 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("RESOLVER", LOG_SPARSE, "Resolver: Internal error starting DNS - hostnames will NOT resolve."); + SocketEngine::Close(this->GetFd()); + this->SetFd(-1); + } + } + else + { + ServerInstance->Logs->Log("RESOLVER", LOG_SPARSE, "Resolver: 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) + diff --git a/src/coremods/core_hostname_lookup.cpp b/src/coremods/core_hostname_lookup.cpp new file mode 100644 index 000000000..3d3e9703a --- /dev/null +++ b/src/coremods/core_hostname_lookup.cpp @@ -0,0 +1,233 @@ +/* + * 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("RESOLVER", 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("RESOLVER", 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("RESOLVER", 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->statsDnsBad++; + } + } +}; + +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->statsDnsBad++; + } + } + + 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) diff --git a/src/commands/cmd_admin.cpp b/src/coremods/core_info/cmd_admin.cpp index 0d6c235f0..722ef8668 100644 --- a/src/commands/cmd_admin.cpp +++ b/src/coremods/core_info/cmd_admin.cpp @@ -19,37 +19,14 @@ #include "inspircd.h" +#include "core_info.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 +CommandAdmin::CommandAdmin(Module* parent) + : Command(parent, "ADMIN", 0, 0) { - 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; - } -}; + Penalty = 2; + syntax = "[<servername>]"; +} /** Handle /ADMIN */ @@ -59,14 +36,19 @@ CmdResult CommandAdmin::Handle (const std::vector<std::string>& parameters, User 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()) + if (!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()); + 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(), ServerInstance->Config->AdminNick.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(), ServerInstance->Config->AdminEmail.c_str()); + RPL_ADMINEMAIL, user->nick.c_str(), AdminEmail.c_str()); return CMD_SUCCESS; } -COMMAND_INIT(CommandAdmin) +RouteDescriptor CommandAdmin::GetRouting(User* user, const std::vector<std::string>& parameters) +{ + if (parameters.size() > 0) + return ROUTE_UNICAST(parameters[0]); + return ROUTE_LOCALONLY; +} diff --git a/src/commands/cmd_commands.cpp b/src/coremods/core_info/cmd_commands.cpp index 1555b4d04..9ae258a9c 100644 --- a/src/commands/cmd_commands.cpp +++ b/src/coremods/core_info/cmd_commands.cpp @@ -19,30 +19,13 @@ #include "inspircd.h" +#include "core_info.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 +CommandCommands::CommandCommands(Module* parent) + : Command(parent, "COMMANDS", 0, 0) { - 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); -}; + Penalty = 3; +} /** Handle /COMMANDS */ @@ -57,18 +40,13 @@ CmdResult CommandCommands::Handle (const std::vector<std::string>&, User *user) 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); + 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)); } 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()); + user->WriteNumeric(RPL_COMMANDSEND, ":End of COMMANDS list"); return CMD_SUCCESS; } - -COMMAND_INIT(CommandCommands) diff --git a/src/commands/cmd_info.cpp b/src/coremods/core_info/cmd_info.cpp index 76e414b19..0d8c1a45a 100644 --- a/src/commands/cmd_info.cpp +++ b/src/coremods/core_info/cmd_info.cpp @@ -21,37 +21,14 @@ #include "inspircd.h" +#include "core_info.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 +CommandInfo::CommandInfo(Module* parent) + : Command(parent, "INFO") { - 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; - } -}; + Penalty = 4; + syntax = "[<servername>]"; +} static const char* const lines[] = { " -/\\- \2InspIRCd\2 -\\/-", @@ -105,9 +82,14 @@ CmdResult CommandInfo::Handle (const std::vector<std::string>& parameters, User 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)); + 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; } -COMMAND_INIT(CommandInfo) +RouteDescriptor CommandInfo::GetRouting(User* user, const std::vector<std::string>& parameters) +{ + if (parameters.size() > 0) + return ROUTE_UNICAST(parameters[0]); + return ROUTE_LOCALONLY; +} diff --git a/src/commands/cmd_modules.cpp b/src/coremods/core_info/cmd_modules.cpp index 2a15b43ed..cee370870 100644 --- a/src/commands/cmd_modules.cpp +++ b/src/coremods/core_info/cmd_modules.cpp @@ -20,50 +20,40 @@ #include "inspircd.h" +#include "core_info.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 +CommandModules::CommandModules(Module* parent) + : Command(parent, "MODULES", 0, 0) { - 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; - } -}; + Penalty = 4; + syntax = "[<servername>]"; +} /** 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; + // 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; + } - std::vector<std::string> module_names = ServerInstance->Modules->GetAllModuleNames(0); + // From an oper and not for us, forward + if (!for_us) + return CMD_SUCCESS; + } - for (unsigned int i = 0; i < module_names.size(); i++) + const ModuleManager::ModuleMap& mods = ServerInstance->Modules->GetModules(); + + for (ModuleManager::ModuleMap::const_iterator i = mods.begin(); i != mods.end(); ++i) { - Module* m = ServerInstance->Modules->Find(module_names[i]); + Module* m = i->second; Version V = m->GetVersion(); if (IS_LOCAL(user) && user->HasPrivPermission("servers/auspex")) @@ -76,17 +66,17 @@ CmdResult CommandModules::Handle (const std::vector<std::string>& parameters, Us #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()); + 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, module_names[i].c_str(), flags.c_str(), V.description.c_str(), srcrev.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(), module_names[i].c_str(), V.description.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()); @@ -94,4 +84,9 @@ CmdResult CommandModules::Handle (const std::vector<std::string>& parameters, Us return CMD_SUCCESS; } -COMMAND_INIT(CommandModules) +RouteDescriptor CommandModules::GetRouting(User* user, const std::vector<std::string>& parameters) +{ + if (parameters.size() >= 1) + return ROUTE_UNICAST(parameters[0]); + return ROUTE_LOCALONLY; +} diff --git a/src/commands/cmd_motd.cpp b/src/coremods/core_info/cmd_motd.cpp index 8e227723e..4481e2d53 100644 --- a/src/commands/cmd_motd.cpp +++ b/src/coremods/core_info/cmd_motd.cpp @@ -19,32 +19,13 @@ #include "inspircd.h" +#include "core_info.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 +CommandMotd::CommandMotd(Module* parent) + : Command(parent, "MOTD", 0, 1) { - 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; - } -}; + syntax = "[<servername>]"; +} /** Handle /MOTD */ @@ -54,8 +35,9 @@ CmdResult CommandMotd::Handle (const std::vector<std::string>& parameters, User return CMD_SUCCESS; ConfigTag* tag = NULL; - if (IS_LOCAL(user)) - tag = user->GetClass()->config; + 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()) @@ -76,4 +58,9 @@ CmdResult CommandMotd::Handle (const std::vector<std::string>& parameters, User return CMD_SUCCESS; } -COMMAND_INIT(CommandMotd) +RouteDescriptor CommandMotd::GetRouting(User* user, const std::vector<std::string>& parameters) +{ + if (parameters.size() > 0) + return ROUTE_UNICAST(parameters[0]); + return ROUTE_LOCALONLY; +} diff --git a/src/coremods/core_info/cmd_time.cpp b/src/coremods/core_info/cmd_time.cpp new file mode 100644 index 000000000..6a10dc327 --- /dev/null +++ b/src/coremods/core_info/cmd_time.cpp @@ -0,0 +1,50 @@ +/* + * 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; + + time_t local = ServerInstance->Time(); + struct tm* timeinfo = localtime(&local); + const std::string& humanTime = asctime(timeinfo); + + user->SendText(":%s %03d %s %s :%s", ServerInstance->Config->ServerName.c_str(), RPL_TIME, user->nick.c_str(), + ServerInstance->Config->ServerName.c_str(), humanTime.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; +} diff --git a/src/modules/m_spanningtree/operquit.cpp b/src/coremods/core_info/cmd_version.cpp index af2e04ebc..eb3ab2c4e 100644 --- a/src/modules/m_spanningtree/operquit.cpp +++ b/src/coremods/core_info/cmd_version.cpp @@ -1,7 +1,8 @@ /* * InspIRCd -- Internet Relay Chat Daemon * - * Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net> + * 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 @@ -18,28 +19,22 @@ #include "inspircd.h" -#include "xline.h" +#include "core_info.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) +CommandVersion::CommandVersion(Module* parent) + : Command(parent, "VERSION", 0, 0) { - if (params.size() < 1) - return true; - - User* u = ServerInstance->FindUUID(prefix); + syntax = "[<servername>]"; +} - if ((u) && (!IS_SERVER(u))) +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->OperQuit.set(u, params[0]); - params[0] = ":" + params[0]; - Utils->DoOneToAllButSender(prefix,"OPERQUIT",params,prefix); + ServerInstance->ISupport.SendTo(lu); } - return true; + return CMD_SUCCESS; } - diff --git a/src/coremods/core_info/core_info.cpp b/src/coremods/core_info/core_info.cpp new file mode 100644 index 000000000..56c3c956d --- /dev/null +++ b/src/coremods/core_info/core_info.cpp @@ -0,0 +1,53 @@ +/* + * 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) diff --git a/src/coremods/core_info/core_info.h b/src/coremods/core_info/core_info.h new file mode 100644 index 000000000..f5dd9e648 --- /dev/null +++ b/src/coremods/core_info/core_info.h @@ -0,0 +1,161 @@ +/* + * 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); +}; diff --git a/src/commands/cmd_ison.cpp b/src/coremods/core_ison.cpp index 01d12e13b..53d2e1c49 100644 --- a/src/commands/cmd_ison.cpp +++ b/src/coremods/core_ison.cpp @@ -20,10 +20,7 @@ #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. +/** Handle /ISON. */ class CommandIson : public Command { @@ -34,8 +31,7 @@ class CommandIson : public Command syntax = "<nick> {nick}"; } /** Handle command. - * @param parameters The parameters to the comamnd - * @param pcnt The number of parameters passed to teh 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. */ diff --git a/src/commands/cmd_list.cpp b/src/coremods/core_list.cpp index 2f417bc04..505b0764c 100644 --- a/src/commands/cmd_list.cpp +++ b/src/coremods/core_list.cpp @@ -20,20 +20,26 @@ #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. +/** Handle /LIST. */ class CommandList : public Command { + ChanModeReference secretmode; + ChanModeReference privatemode; + public: /** Constructor for list. */ - CommandList ( Module* parent) : Command(parent,"LIST", 0, 0) { Penalty = 5; } + CommandList(Module* parent) + : Command(parent,"LIST", 0, 0) + , secretmode(creator, "secret") + , privatemode(creator, "private") + { + Penalty = 5; + } + /** Handle command. - * @param parameters The parameters to the comamnd - * @param pcnt The number of parameters passed to teh 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. */ @@ -47,7 +53,7 @@ CmdResult CommandList::Handle (const std::vector<std::string>& parameters, User { int minusers = 0, maxusers = 0; - user->WriteNumeric(321, "%s Channel :Users Name",user->nick.c_str()); + user->WriteNumeric(RPL_LISTSTART, "Channel :Users Name"); /* Work around mIRC suckyness. YOU SUCK, KHALED! */ if (parameters.size() == 1) @@ -62,7 +68,8 @@ CmdResult CommandList::Handle (const std::vector<std::string>& parameters, User } } - for (chan_hash::const_iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); i++) + const chan_hash& chans = ServerInstance->GetChans(); + for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i) { // attempt to match a glob pattern long users = i->second->GetUserCounter(); @@ -82,21 +89,21 @@ CmdResult CommandList::Handle (const std::vector<std::string>& parameters, User // 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')) + if (!n && i->second->IsModeSet(privatemode)) { /* Channel is +p and user is outside/not privileged */ - user->WriteNumeric(322, "%s * %ld :",user->nick.c_str(), users); + user->WriteNumeric(RPL_LIST, "* %ld :", users); } else { - if (n || !i->second->IsModeSet('s')) + if (n || !i->second->IsModeSet(secretmode)) { /* 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(RPL_LIST, "%s %ld :[+%s] %s",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()); + user->WriteNumeric(RPL_LISTEND, ":End of channel list."); return CMD_SUCCESS; } diff --git a/src/coremods/core_loadmodule.cpp b/src/coremods/core_loadmodule.cpp new file mode 100644 index 000000000..1d49d89d0 --- /dev/null +++ b/src/coremods/core_loadmodule.cpp @@ -0,0 +1,126 @@ +/* + * 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) diff --git a/src/commands/cmd_lusers.cpp b/src/coremods/core_lusers.cpp index 91a718090..8cc603eee 100644 --- a/src/commands/cmd_lusers.cpp +++ b/src/coremods/core_lusers.cpp @@ -26,10 +26,10 @@ struct LusersCounters unsigned int max_global; unsigned int invisible; - LusersCounters() + LusersCounters(unsigned int inv) : max_local(ServerInstance->Users->LocalUserCount()) , max_global(ServerInstance->Users->RegisteredUserCount()) - , invisible(ServerInstance->Users->ModeCount('i')) + , invisible(inv) { } @@ -45,10 +45,7 @@ struct LusersCounters } }; -/** 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. +/** Handle /LUSERS. */ class CommandLusers : public Command { @@ -60,8 +57,7 @@ class CommandLusers : public Command : 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 parameters The parameters to the command * @param user The user issuing the command * @return A value from CmdResult to indicate command success or failure. */ @@ -73,11 +69,11 @@ class CommandLusers : public Command CmdResult CommandLusers::Handle (const std::vector<std::string>&, User *user) { unsigned int n_users = ServerInstance->Users->RegisteredUserCount(); - ProtoServerList serverlist; + ProtocolInterface::ServerList 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) + for (ProtocolInterface::ServerList::const_iterator i = serverlist.begin(); i != serverlist.end(); ++i) { if (i->parentname == ServerInstance->Config->ServerName) n_local_servs++; @@ -88,19 +84,19 @@ CmdResult CommandLusers::Handle (const std::vector<std::string>&, User *user) counters.UpdateMaxUsers(); - user->WriteNumeric(251, "%s :There are %d users and %d invisible on %d servers",user->nick.c_str(), + 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(252, "%s %d :operator(s) online",user->nick.c_str(),ServerInstance->Users->OperCount()); + user->WriteNumeric(RPL_LUSEROP, "%d :operator(s) online", ServerInstance->Users->OperCount()); if (ServerInstance->Users->UnregisteredUserCount()) - user->WriteNumeric(253, "%s %d :unknown connections",user->nick.c_str(),ServerInstance->Users->UnregisteredUserCount()); + user->WriteNumeric(RPL_LUSERUNKNOWN, "%d :unknown connections", 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); + 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; } @@ -110,11 +106,11 @@ class InvisibleWatcher : public ModeWatcher unsigned int& invisible; public: InvisibleWatcher(Module* mod, unsigned int& Invisible) - : ModeWatcher(mod, 'i', MODETYPE_USER), invisible(Invisible) + : ModeWatcher(mod, "invisible", MODETYPE_USER), invisible(Invisible) { } - void AfterMode(User* source, User* dest, Channel* channel, const std::string& parameter, bool adding, ModeType type) + void AfterMode(User* source, User* dest, Channel* channel, const std::string& parameter, bool adding) { if (dest->registered != REG_ALL) return; @@ -128,42 +124,46 @@ public: class ModuleLusers : public Module { + UserModeReference invisiblemode; LusersCounters counters; CommandLusers cmd; InvisibleWatcher mw; - public: - ModuleLusers() - : cmd(this, counters), mw(this, counters.invisible) + 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; } - void init() + public: + ModuleLusers() + : invisiblemode(this, "invisible") + , counters(CountInvisible()) + , cmd(this, counters) + , mw(this, counters.invisible) { - 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')) + if (user->IsModeSet(invisiblemode)) counters.invisible++; } void OnUserQuit(User* user, const std::string& message, const std::string& oper_message) { - if (user->IsModeSet('i')) + if (user->IsModeSet(invisiblemode)) counters.invisible--; } - ~ModuleLusers() - { - ServerInstance->Modes->DelModeWatcher(&mw); - } - Version GetVersion() { return Version("LUSERS", VF_VENDOR | VF_CORE); diff --git a/src/commands/cmd_die.cpp b/src/coremods/core_oper/cmd_die.cpp index 1d6640213..5a9415915 100644 --- a/src/commands/cmd_die.cpp +++ b/src/coremods/core_oper/cmd_die.cpp @@ -19,38 +19,25 @@ #include "inspircd.h" +#include "exitcodes.h" +#include "core_oper.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 +CommandDie::CommandDie(Module* parent) + : Command(parent, "DIE", 1) { - 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" + flags_needed = 'o'; + syntax = "<password>"; +} /** 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)) + if (DieRestart::CheckPass(user, parameters[0], "diepass")) { { std::string diebuf = "*** DIE command from " + user->GetFullHost() + ". Terminating."; - ServerInstance->Logs->Log("COMMAND",SPARSE, diebuf); + ServerInstance->Logs->Log("COMMAND", LOG_SPARSE, diebuf); ServerInstance->SendError(diebuf); } @@ -58,11 +45,9 @@ CmdResult CommandDie::Handle (const std::vector<std::string>& parameters, User * } else { - ServerInstance->Logs->Log("COMMAND",SPARSE, "Failed /DIE command from %s", user->GetFullRealHost().c_str()); + 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; } - -COMMAND_INIT(CommandDie) diff --git a/src/commands/cmd_kill.cpp b/src/coremods/core_oper/cmd_kill.cpp index 17c8a76a0..b60885c64 100644 --- a/src/commands/cmd_kill.cpp +++ b/src/coremods/core_oper/cmd_kill.cpp @@ -20,45 +20,28 @@ #include "inspircd.h" +#include "core_oper.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 +CommandKill::CommandKill(Module* parent) + : Command(parent, "KILL", 2, 2) { - 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; - } -}; + 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 (ServerInstance->Parser->LoopCall(user, this, parameters, 0)) - return CMD_SUCCESS; + 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))) @@ -71,7 +54,6 @@ CmdResult CommandKill::Handle (const std::vector<std::string>& parameters, User * just gets processed and passed on, otherwise, if they are local, it gets prefixed. Makes sense :-) -- w00t */ - std::string killreason; if (IS_LOCAL(user)) { /* @@ -111,8 +93,9 @@ CmdResult CommandKill::Handle (const std::vector<std::string>& parameters, User 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)); + 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 { @@ -121,23 +104,24 @@ CmdResult CommandKill::Handle (const std::vector<std::string>& parameters, User * 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) + if (!user->server->IsULine()) { - 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()); + 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 @@ -145,12 +129,28 @@ CmdResult CommandKill::Handle (const std::vector<std::string>& parameters, User } else { - user->WriteServ( "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; } 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; +} + -COMMAND_INIT(CommandKill) +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); +} diff --git a/src/commands/cmd_oper.cpp b/src/coremods/core_oper/cmd_oper.cpp index 1a5e7e178..8c0d05ce2 100644 --- a/src/commands/cmd_oper.cpp +++ b/src/coremods/core_oper/cmd_oper.cpp @@ -20,53 +20,22 @@ #include "inspircd.h" +#include "core_oper.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 +CommandOper::CommandOper(Module* parent) + : SplitCommand(parent, "OPER", 2, 2) { - 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; + syntax = "<username> <password>"; } 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()); + const std::string userHost = user->ident + "@" + user->host; + const std::string userIP = user->ident + "@" + user->GetIPString(); OperIndex::iterator i = ServerInstance->Config->oper_blocks.find(parameters[0]); if (i != ServerInstance->Config->oper_blocks.end()) @@ -74,8 +43,8 @@ CmdResult CommandOper::HandleLocal(const std::vector<std::string>& parameters, L 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")); + 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) { @@ -94,12 +63,10 @@ CmdResult CommandOper::HandleLocal(const std::vector<std::string>& parameters, L 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->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",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()); + 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; } - -COMMAND_INIT(CommandOper) diff --git a/src/commands/cmd_rehash.cpp b/src/coremods/core_oper/cmd_rehash.cpp index abf0b7876..48dfa6fb1 100644 --- a/src/commands/cmd_rehash.cpp +++ b/src/coremods/core_oper/cmd_rehash.cpp @@ -20,32 +20,21 @@ #include "inspircd.h" +#include "core_oper.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 +CommandRehash::CommandRehash(Module* parent) + : Command(parent, "REHASH", 0) { - 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); -}; + 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(I_OnPreRehash,OnPreRehash(user, param)); + FOREACH_MOD(OnPreRehash, (user, param)); if (param.empty()) { @@ -68,32 +57,25 @@ CmdResult CommandRehash::Handle (const std::vector<std::string>& parameters, Use if (param[0] == '-') param = param.substr(1); - FOREACH_MOD(I_OnModuleRehash,OnModuleRehash(user, param)); + 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 " + ServerConfig::CleanFilename(ServerInstance->ConfigFileName.c_str()) + " on " + ServerInstance->Config->ServerName; + 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 %s :Rehashing", - user->nick.c_str(),ServerConfig::CleanFilename(ServerInstance->ConfigFileName.c_str())); + user->WriteNumeric(RPL_REHASHING, "%s :Rehashing", FileSystem::GetFileName(ServerInstance->ConfigFileName).c_str()); else - ServerInstance->PI->SendUserNotice(user, std::string("*** Rehashing server ") + - ServerConfig::CleanFilename(ServerInstance->ConfigFileName.c_str())); + 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->RehashUsersAndChans(); - FOREACH_MOD(I_OnGarbageCollect, OnGarbageCollect()); - - - ServerInstance->ConfigThread = new ConfigReaderThread(user->uuid); - ServerInstance->Threads->Start(ServerInstance->ConfigThread); + ServerInstance->Rehash(user->uuid); } else { @@ -102,7 +84,7 @@ CmdResult CommandRehash::Handle (const std::vector<std::string>& parameters, Use * 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()); + 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."); } @@ -110,6 +92,3 @@ CmdResult CommandRehash::Handle (const std::vector<std::string>& parameters, Use // Always return success so spanningtree forwards an incoming REHASH even if we failed return CMD_SUCCESS; } - - -COMMAND_INIT(CommandRehash) diff --git a/src/commands/cmd_restart.cpp b/src/coremods/core_oper/cmd_restart.cpp index bdbcfed35..4fad752a2 100644 --- a/src/commands/cmd_restart.cpp +++ b/src/coremods/core_oper/cmd_restart.cpp @@ -19,28 +19,19 @@ #include "inspircd.h" +#include "core_oper.h" -/** Handle /RESTART - */ -class CommandRestart : public Command +CommandRestart::CommandRestart(Module* parent) + : Command(parent, "RESTART", 1, 1) { - 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); -}; + flags_needed = 'o'; + syntax = "<password>"; +} 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->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()); @@ -71,6 +62,3 @@ CmdResult CommandRestart::Handle (const std::vector<std::string>& parameters, Us } return CMD_FAILURE; } - - -COMMAND_INIT(CommandRestart) diff --git a/src/coremods/core_oper/core_oper.cpp b/src/coremods/core_oper/core_oper.cpp new file mode 100644 index 000000000..0fc82df8f --- /dev/null +++ b/src/coremods/core_oper/core_oper.cpp @@ -0,0 +1,55 @@ +/* + * 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) diff --git a/src/coremods/core_oper/core_oper.h b/src/coremods/core_oper/core_oper.h new file mode 100644 index 000000000..3b3dfd4b2 --- /dev/null +++ b/src/coremods/core_oper/core_oper.h @@ -0,0 +1,124 @@ +/* + * 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); +}; diff --git a/src/coremods/core_privmsg.cpp b/src/coremods/core_privmsg.cpp new file mode 100644 index 000000000..34953bbe8 --- /dev/null +++ b/src/coremods/core_privmsg.cpp @@ -0,0 +1,296 @@ +/* + * 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 LocalUserList& list = ServerInstance->Users->local_users; + for (LocalUserList::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) diff --git a/src/commands/cmd_reloadmodule.cpp b/src/coremods/core_reloadmodule.cpp index 81ecce1cb..1561131dc 100644 --- a/src/commands/cmd_reloadmodule.cpp +++ b/src/coremods/core_reloadmodule.cpp @@ -27,8 +27,7 @@ class CommandReloadmodule : public Command */ 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 parameters The parameters to the command * @param user The user issuing the command * @return A value from CmdResult to indicate command success or failure. */ @@ -48,22 +47,22 @@ class ReloadModuleWorker : public HandlerBase1<void, bool> 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"); + 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) { - if (parameters[0] == "cmd_reloadmodule.so") + Module* m = ServerInstance->Modules->Find(parameters[0]); + if (m == creator) { - user->WriteNumeric(975, "%s %s :You cannot reload cmd_reloadmodule.so (unload and load it)", - user->nick.c_str(), parameters[0].c_str()); + user->WriteNumeric(RPL_LOADEDMODULE, "%s :You cannot reload core_reloadmodule.so (unload and load it)", + 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])); @@ -71,7 +70,7 @@ CmdResult CommandReloadmodule::Handle (const std::vector<std::string>& parameter } 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; } } diff --git a/src/commands/cmd_stats.cpp b/src/coremods/core_stats.cpp index 23143442a..a593ac94d 100644 --- a/src/commands/cmd_stats.cpp +++ b/src/coremods/core_stats.cpp @@ -21,17 +21,13 @@ #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. +/** Handle /STATS. */ class CommandStats : public Command { @@ -41,8 +37,7 @@ class CommandStats : public Command */ 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 parameters The parameters to the command * @param user The user issuing the command * @return A value from CmdResult to indicate command success or failure. */ @@ -57,10 +52,8 @@ class CommandStats : public Command 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 isRemoteOper = IS_REMOTE(user) && (user->IsOper()); bool isLocalOperWithPrivs = IS_LOCAL(user) && user->HasPrivPermission("servers/auspex"); if (!isPublic && !isRemoteOper && !isLocalOperWithPrivs) @@ -69,7 +62,7 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results) "%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."); + results.push_back("481 " + user->nick + " :Permission denied - STATS " + statschar + " requires the servers/auspex priv."); return; } @@ -77,7 +70,7 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results) 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"); + 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; @@ -97,7 +90,7 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results) 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)+ + results.push_back("249 "+user->nick+" :"+ ip + ":"+ConvToStr(ls->bind_port)+ " (" + type + ", " + hook + ")"); } } @@ -114,7 +107,7 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results) { ConnectClass* c = *i; std::stringstream res; - res << sn << " 215 " << user->nick << " I " << c->name << ' '; + res << "215 " << user->nick << " I " << c->name << ' '; if (c->type == CC_ALLOW) res << '+'; if (c->type == CC_DENY) @@ -142,37 +135,30 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results) 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())+" :"+ + 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 '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) + 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)) + if (!oper->server->IsULine()) { - 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")); + 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(sn+" 249 "+user->nick+" :"+ConvToStr(idx)+" OPER(s)"); + results.push_back("249 "+user->nick+" :"+ConvToStr(idx)+" OPER(s)"); } break; @@ -192,11 +178,14 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results) 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; + { + 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': @@ -205,7 +194,7 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results) 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)); + results.push_back("212 "+user->nick+" "+i->second->name+" "+ConvToStr(i->second->use_count)); } } break; @@ -213,34 +202,22 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results) /* 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); - } - } + 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->cmdlist.size())); 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); + 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(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"); + 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. @@ -251,11 +228,11 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results) /* 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)); + 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]; @@ -265,27 +242,27 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results) float per = (n_eaten / n_elapsed) * 100; snprintf(percent, 30, "%03.5f%%", per); - results.push_back(sn+" 249 "+user->nick+" :CPU Use (now): "+percent); + 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(sn+" 249 "+user->nick+" :CPU Use (total): "+percent); + results.push_back("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)); + 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; + FILETIME ExitTime; + FILETIME KernelTime; + FILETIME UserTime; LARGE_INTEGER ThisSample; if(GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime, &KernelTime, &UserTime) && QueryPerformanceCounter(&ThisSample)) @@ -295,17 +272,17 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results) 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); + 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(sn+" 249 "+user->nick+" :CPU Use (total): "+percent); + results.push_back("249 "+user->nick+" :CPU Use (total): "+percent); } #endif } @@ -313,15 +290,13 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results) 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); + results.push_back("249 "+user->nick+" :accepts "+ConvToStr(ServerInstance->stats->statsAccept)+" refused "+ConvToStr(ServerInstance->stats->statsRefused)); + results.push_back("249 "+user->nick+" :unknown commands "+ConvToStr(ServerInstance->stats->statsUnknown)); + results.push_back("249 "+user->nick+" :nick collisions "+ConvToStr(ServerInstance->stats->statsCollisions)); + results.push_back("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("249 "+user->nick+" :connection count "+ConvToStr(ServerInstance->stats->statsConnects)); + results.push_back(InspIRCd::Format("249 %s :bytes sent %5.2fK recv %5.2fK", user->nick.c_str(), + ServerInstance->stats->statsSent / 1024.0, ServerInstance->stats->statsRecv / 1024.0)); } break; @@ -332,18 +307,15 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results) for(ConfigIter i = tags.first; i != tags.second; ++i) { ConfigTag* tag = i->second; - results.push_back(sn+" 243 "+user->nick+" O "+tag->getString("host")+" * "+ + results.push_back("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++) + for (OperIndex::const_iterator i = ServerInstance->Config->OperTypes.begin(); i != ServerInstance->Config->OperTypes.end(); ++i) { - // just the types, not the actual oper blocks... - if (i->first[0] != ' ') - continue; OperInfo* tag = i->second; tag->init(); std::string umodes; @@ -357,28 +329,28 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results) if (mh && mh->NeedsOper() && tag->AllowedChanModes[c - 'A']) cmodes.push_back(c); } - results.push_back(sn+" 243 "+user->nick+" O "+tag->NameStr() + " " + umodes + " " + cmodes); + results.push_back("243 "+user->nick+" O "+tag->name.c_str() + " " + 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"); + results.push_back("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)); + results.push_back("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"); + results.push_back("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)); + results.push_back("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; @@ -394,15 +366,15 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results) * 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); + results.push_back(InspIRCd::Format("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)); } 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); + results.push_back(InspIRCd::Format("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)); } } break; @@ -411,7 +383,7 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results) break; } - results.push_back(sn+" 219 "+user->nick+" "+statschar+" :End of /STATS report"); + 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; @@ -424,8 +396,10 @@ CmdResult CommandStats::Handle (const std::vector<std::string>& parameters, User 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(":%s", values[i].c_str()); + user->SendText(p + values[i]); return CMD_SUCCESS; } diff --git a/src/coremods/core_stub.cpp b/src/coremods/core_stub.cpp new file mode 100644 index 000000000..30c7ce752 --- /dev/null +++ b/src/coremods/core_stub.cpp @@ -0,0 +1,156 @@ +/* + * 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> [<remote-server>]"; + } + + /** 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> [<reason>]"; + } + + /** 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) diff --git a/src/commands/cmd_away.cpp b/src/coremods/core_user/cmd_away.cpp index fa3f7fae9..adc6e6c18 100644 --- a/src/commands/cmd_away.cpp +++ b/src/coremods/core_user/cmd_away.cpp @@ -19,26 +19,13 @@ #include "inspircd.h" +#include "core_user.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 +CommandAway::CommandAway(Module* parent) + : Command(parent, "AWAY", 0, 0) { - 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); -}; + syntax = "[<message>]"; +} /** Handle /AWAY */ @@ -56,7 +43,7 @@ CmdResult CommandAway::Handle (const std::vector<std::string>& parameters, User 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()); + user->WriteNumeric(RPL_NOWAWAY, ":You have been marked as being away"); } else { @@ -66,10 +53,13 @@ CmdResult CommandAway::Handle (const std::vector<std::string>& parameters, User return CMD_FAILURE; user->awaymsg.clear(); - user->WriteNumeric(RPL_UNAWAY, "%s :You are no longer marked as being away",user->nick.c_str()); + user->WriteNumeric(RPL_UNAWAY, ":You are no longer marked as being away"); } return CMD_SUCCESS; } -COMMAND_INIT(CommandAway) +RouteDescriptor CommandAway::GetRouting(User* user, const std::vector<std::string>& parameters) +{ + return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST); +} diff --git a/src/commands/cmd_nick.cpp b/src/coremods/core_user/cmd_nick.cpp index a079e59d0..166941c6d 100644 --- a/src/commands/cmd_nick.cpp +++ b/src/coremods/core_user/cmd_nick.cpp @@ -21,26 +21,15 @@ #include "inspircd.h" +#include "core_user.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 +CommandNick::CommandNick(Module* parent) + : Command(parent, "NICK", 1, 1) { - 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); -}; + 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 @@ -58,7 +47,7 @@ CmdResult CommandNick::Handle (const std::vector<std::string>& parameters, User if (newnick.empty()) { - user->WriteNumeric(432, "%s * :Erroneous Nickname", oldnick.c_str()); + user->WriteNumeric(ERR_ERRONEUSNICKNAME, "* :Erroneous Nickname"); return CMD_FAILURE; } @@ -66,9 +55,9 @@ CmdResult CommandNick::Handle (const std::vector<std::string>& parameters, User { newnick = user->uuid; } - else if (!ServerInstance->IsNick(newnick.c_str(), ServerInstance->Config->Limits.NickMax)) + else if (!ServerInstance->IsNick(newnick)) { - user->WriteNumeric(432, "%s %s :Erroneous Nickname", user->nick.c_str(),newnick.c_str()); + user->WriteNumeric(ERR_ERRONEUSNICKNAME, "%s :Erroneous Nickname", newnick.c_str()); return CMD_FAILURE; } @@ -93,6 +82,3 @@ CmdResult CommandNick::Handle (const std::vector<std::string>& parameters, User return CMD_SUCCESS; } - - -COMMAND_INIT(CommandNick) diff --git a/src/coremods/core_user/cmd_part.cpp b/src/coremods/core_user/cmd_part.cpp new file mode 100644 index 000000000..9f82c15a5 --- /dev/null +++ b/src/coremods/core_user/cmd_part.cpp @@ -0,0 +1,63 @@ +/* + * 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); +} diff --git a/src/coremods/core_user/cmd_quit.cpp b/src/coremods/core_user/cmd_quit.cpp new file mode 100644 index 000000000..c4e127dd8 --- /dev/null +++ b/src/coremods/core_user/cmd_quit.cpp @@ -0,0 +1,50 @@ +/* + * 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); +} diff --git a/src/commands/cmd_user.cpp b/src/coremods/core_user/cmd_user.cpp index 305d0847f..6de762e44 100644 --- a/src/commands/cmd_user.cpp +++ b/src/coremods/core_user/cmd_user.cpp @@ -19,39 +19,28 @@ #include "inspircd.h" +#include "core_user.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 +CommandUser::CommandUser(Module* parent) + : SplitCommand(parent, "USER", 4, 4) { - 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); -}; + 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].c_str())) + 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(461, "%s USER :Your username is not valid",user->nick.c_str()); + user->WriteNumeric(ERR_NEEDMOREPARAMS, "USER :Your username is not valid"); return CMD_FAILURE; } else @@ -61,14 +50,14 @@ CmdResult CommandUser::HandleLocal(const std::vector<std::string>& parameters, L * ~ character, and +1 for null termination, therefore we can safely use up to * IDENTMAX here. */ - user->ChangeIdent(parameters[0].c_str()); + 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(462, "%s :You may not reregister", user->nick.c_str()); + user->WriteNumeric(ERR_ALREADYREGISTERED, ":You may not reregister"); return CMD_FAILURE; } @@ -86,5 +75,3 @@ CmdResult CommandUser::HandleLocal(const std::vector<std::string>& parameters, L return CMD_SUCCESS; } - -COMMAND_INIT(CommandUser) diff --git a/src/coremods/core_user/core_user.cpp b/src/coremods/core_user/core_user.cpp new file mode 100644 index 000000000..103880a6e --- /dev/null +++ b/src/coremods/core_user/core_user.cpp @@ -0,0 +1,191 @@ +/* + * 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" + +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 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) + { + ServerInstance->Modes->Process(parameters, user, (IS_LOCAL(user) ? ModeParser::MODE_NONE : ModeParser::MODE_LOCALONLY)); + return CMD_SUCCESS; + } + + RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) + { + return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST); + } +}; + +/** 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) diff --git a/src/coremods/core_user/core_user.h b/src/coremods/core_user/core_user.h new file mode 100644 index 000000000..9c63e6592 --- /dev/null +++ b/src/coremods/core_user/core_user.h @@ -0,0 +1,138 @@ +/* + * 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); +}; + +/** Handle /NICK. + */ +class CommandNick : public Command +{ + 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 Handle(const std::vector<std::string>& parameters, User *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); +}; diff --git a/src/commands/cmd_userhost.cpp b/src/coremods/core_userhost.cpp index 399de0b1a..541402c10 100644 --- a/src/commands/cmd_userhost.cpp +++ b/src/coremods/core_userhost.cpp @@ -20,10 +20,7 @@ #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. +/** Handle /USERHOST. */ class CommandUserhost : public Command { @@ -34,8 +31,7 @@ class CommandUserhost : public Command syntax = "<nick> {<nick>}"; } /** Handle command. - * @param parameters The parameters to the comamnd - * @param pcnt The number of parameters passed to teh 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. */ @@ -54,12 +50,12 @@ CmdResult CommandUserhost::Handle (const std::vector<std::string>& parameters, U { retbuf = retbuf + u->nick; - if (IS_OPER(u)) + if (u->IsOper()) retbuf = retbuf + "*"; retbuf = retbuf + "="; - if (IS_AWAY(u)) + if (u->IsAway()) retbuf += "-"; else retbuf += "+"; diff --git a/src/commands/cmd_wallops.cpp b/src/coremods/core_wallops.cpp index 198997a95..5dfb79b67 100644 --- a/src/commands/cmd_wallops.cpp +++ b/src/coremods/core_wallops.cpp @@ -20,24 +20,34 @@ #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. +/** Handle /WALLOPS. */ class CommandWallops : public Command { + SimpleUserModeHandler wallopsmode; + public: /** Constructor for wallops. */ - CommandWallops ( Module* parent) : Command(parent,"WALLOPS",1,1) { flags_needed = 'o'; syntax = "<any-text>"; } + 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 comamnd - * @param pcnt The number of parameters passed to teh 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) @@ -48,11 +58,10 @@ CmdResult CommandWallops::Handle (const std::vector<std::string>& parameters, Us 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); + if (t->IsModeSet(wallopsmode)) + t->WriteFrom(user, wallop); } - FOREACH_MOD(I_OnWallops,OnWallops(user,parameters[0])); return CMD_SUCCESS; } diff --git a/src/commands/cmd_who.cpp b/src/coremods/core_who.cpp index 2b3242211..6f4bc088e 100644 --- a/src/commands/cmd_who.cpp +++ b/src/coremods/core_who.cpp @@ -20,10 +20,7 @@ #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. +/** Handle /WHO. */ class CommandWho : public Command { @@ -39,17 +36,36 @@ class CommandWho : public Command bool opt_local; bool opt_far; bool opt_time; + ChanModeReference secretmode; + ChanModeReference privatemode; + UserModeReference invisiblemode; + + Membership* get_first_visible_channel(User* u) + { + for (UCListIter 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) { + 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, Channel* ch, User* u, std::vector<std::string> &whoresults); + + 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 comamnd - * @param pcnt The number of parameters passed to teh 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. */ @@ -57,19 +73,6 @@ class CommandWho : public Command 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; @@ -138,7 +141,7 @@ bool CommandWho::whomatch(User* cuser, User* user, const char* matchtext) match = InspIRCd::Match(user->awaymsg, matchtext); else if (opt_time) { - long seconds = ServerInstance->Duration(matchtext); + long seconds = InspIRCd::Duration(matchtext); // Okay, so time matching, we want all users connected `seconds' ago if (user->age >= ServerInstance->Time() - seconds) @@ -160,7 +163,7 @@ bool CommandWho::whomatch(User* cuser, User* user, const char* 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); + match = InspIRCd::Match(user->server->GetName(), matchtext); return match; } @@ -180,28 +183,28 @@ bool CommandWho::CanView(Channel* chan, User* user) 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')) + 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, Channel* ch, User* u, std::vector<std::string> &whoresults) +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 (!ch) - ch = get_first_visible_channel(u); + if (!memb) + memb = get_first_visible_channel(u); - std::string wholine = initial + (ch ? ch->name : "*") + " " + u->ident + " " + + 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); - + wholine.append(u->server->GetName()); + wholine.append(" " + u->nick + " "); /* away? */ - if (IS_AWAY(u)) + if (u->IsAway()) { wholine.append("G"); } @@ -211,17 +214,21 @@ void CommandWho::SendWhoLine(User* user, const std::vector<std::string>& parms, } /* oper? */ - if (IS_OPER(u)) + if (u->IsOper()) { wholine.push_back('*'); } - if (ch) - wholine.append(ch->GetPrefixChar(u)); + if (memb) + { + char prefix = memb->GetPrefixChar(); + if (prefix) + wholine.push_back(prefix); + } wholine.append(" :0 " + u->fullname); - FOREACH_MOD(I_OnSendWhoLine, OnSendWhoLine(user, parms, u, wholine)); + FOREACH_MOD(OnSendWhoLine, (user, parms, u, memb, wholine)); if (!wholine.empty()) whoresults.push_back(wholine); @@ -249,33 +256,17 @@ CmdResult CommandWho::Handle (const std::vector<std::string>& parameters, User * 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); + std::string matchtext = ((parameters[0] == "0") ? "*" : parameters[0]); - for (const char* check = matchtext; *check; check++) - { - if (*check == '*' || *check == '?' || *check == '.') - { - usingwildcards = true; - break; - } - } + // WHO flags count as a wildcard + bool usingwildcards = ((parameters.size() > 1) || (matchtext.find_first_of("*?.") != std::string::npos)); 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) @@ -325,7 +316,7 @@ CmdResult CommandWho::Handle (const std::vector<std::string>& parameters, User * /* who on a channel? */ - ch = ServerInstance->FindChan(matchtext); + Channel* ch = ServerInstance->FindChan(matchtext); if (ch) { @@ -342,15 +333,15 @@ CmdResult CommandWho::Handle (const std::vector<std::string>& parameters, User * if (user != i->first) { /* opers only, please */ - if (opt_viewopersonly && !IS_OPER(i->first)) + if (opt_viewopersonly && !i->first->IsOper()) continue; /* If we're not inside the channel, hide +i users */ - if (i->first->IsModeSet('i') && !inside && !user->HasPrivPermission("users/auspex")) + if (i->first->IsModeSet(invisiblemode) && !inside && !user->HasPrivPermission("users/auspex")) continue; } - SendWhoLine(user, parameters, initial, ch, i->first, whoresults); + SendWhoLine(user, parameters, initial, i->second, i->first, whoresults); } } } @@ -360,15 +351,16 @@ CmdResult CommandWho::Handle (const std::vector<std::string>& parameters, User * if (opt_viewopersonly) { /* Showing only opers */ - for (std::list<User*>::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 (whomatch(user, oper, matchtext)) + if (whomatch(user, oper, matchtext.c_str())) { if (!user->SharesChannelWith(oper)) { - if (usingwildcards && (oper->IsModeSet('i')) && (!user->HasPrivPermission("users/auspex"))) + if (usingwildcards && (oper->IsModeSet(invisiblemode)) && (!user->HasPrivPermission("users/auspex"))) continue; } @@ -378,13 +370,14 @@ CmdResult CommandWho::Handle (const std::vector<std::string>& parameters, User * } else { - for (user_hash::iterator i = ServerInstance->Users->clientlist->begin(); i != ServerInstance->Users->clientlist->end(); i++) + 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)) + if (whomatch(user, i->second, matchtext.c_str())) { if (!user->SharesChannelWith(i->second)) { - if (usingwildcards && (i->second->IsModeSet('i')) && (!user->HasPrivPermission("users/auspex"))) + if (usingwildcards && (i->second->IsModeSet(invisiblemode)) && (!user->HasPrivPermission("users/auspex"))) continue; } @@ -396,7 +389,7 @@ CmdResult CommandWho::Handle (const std::vector<std::string>& parameters, User * /* 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() : "*"); + 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) diff --git a/src/coremods/core_whois.cpp b/src/coremods/core_whois.cpp new file mode 100644 index 000000000..934dd2102 --- /dev/null +++ b/src/coremods/core_whois.cpp @@ -0,0 +1,244 @@ +/* + * 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 (UCListIter 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 = abs((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) diff --git a/src/coremods/core_whowas.cpp b/src/coremods/core_whowas.cpp new file mode 100644 index 000000000..0227fdb51 --- /dev/null +++ b/src/coremods/core_whowas.cpp @@ -0,0 +1,270 @@ +/* + * 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) + , GroupSize(0), MaxGroups(0), MaxKeep(0) +{ + syntax = "<nick>{,<nick>}"; + Penalty = 2; +} + +CmdResult CommandWhowas::Handle (const std::vector<std::string>& parameters, User* user) +{ + /* if whowas disabled in config */ + if (this->GroupSize == 0 || this->MaxGroups == 0) + { + user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s :This command has been disabled.", name.c_str()); + return CMD_FAILURE; + } + + whowas_users::iterator i = whowas.find(assign(parameters[0])); + + if (i == whowas.end()) + { + user->WriteNumeric(ERR_WASNOSUCHNICK, "%s :There was no such nickname", parameters[0].c_str()); + } + else + { + whowas_set* grp = i->second; + if (!grp->empty()) + { + for (whowas_set::iterator ux = grp->begin(); ux != grp->end(); ux++) + { + WhoWasGroup* u = *ux; + + 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()); + } + } + else + { + user->WriteNumeric(ERR_WASNOSUCHNICK, "%s :There was no such nickname", parameters[0].c_str()); + } + } + + user->WriteNumeric(RPL_ENDOFWHOWAS, "%s :End of WHOWAS", parameters[0].c_str()); + return CMD_SUCCESS; +} + +std::string CommandWhowas::GetStats() +{ + int whowas_size = 0; + int whowas_bytes = 0; + for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i) + { + whowas_set* n = i->second; + 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 (this->GroupSize == 0 || this->MaxGroups == 0) + { + 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(irc::string(user->nick.c_str()), static_cast<whowas_set*>(NULL))); + + if (ret.second) // If inserted + { + // This nick is new, create a list for it and add the first record to it + whowas_set* n = new whowas_set; + n->push_back(new WhoWasGroup(user)); + ret.first->second = n; + + // Add this nick to the fifo too + whowas_fifo.push_back(std::make_pair(ServerInstance->Time(), ret.first->first)); + + 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 + whowas_users::iterator it = whowas.find(whowas_fifo.front().second); + if (it != whowas.end()) + { + whowas_set* set = it->second; + stdalgo::delete_all(*set); + + delete set; + whowas.erase(it); + } + whowas_fifo.pop_front(); + } + } + else + { + // We've met this nick before, add a new record to the list + whowas_set* set = ret.first->second; + set->push_back(new WhoWasGroup(user)); + + // If there are too many records for this nick, remove the oldest (front) + if (set->size() > this->GroupSize) + { + delete set->front(); + set->pop_front(); + } + } +} + +/* on rehash, refactor maps according to new conf values */ +void CommandWhowas::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()) + { + if ((whowas_fifo.size() > this->MaxGroups) || (whowas_fifo.front().first < min)) + { + whowas_users::iterator iter = whowas.find(whowas_fifo.front().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", LOG_DEFAULT, "BUG: Whowas maps got corrupted! (1)"); + return; + } + + whowas_set* set = iter->second; + stdalgo::delete_all(*set); + + delete set; + whowas.erase(iter); + whowas_fifo.pop_front(); + } + else + break; + } + + /* Then cut the whowas sets to new size (groupsize) */ + for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i) + { + whowas_set* n = i->second; + while (n->size() > this->GroupSize) + { + delete n->front(); + n->pop_front(); + } + } +} + +/* call maintain once an hour to remove expired nicks */ +void CommandWhowas::Maintain() +{ + time_t min = ServerInstance->Time() - this->MaxKeep; + for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i) + { + whowas_set* set = i->second; + while (!set->empty() && set->front()->signon < min) + { + delete set->front(); + set->pop_front(); + } + } +} + +CommandWhowas::~CommandWhowas() +{ + for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i) + { + whowas_set* set = i->second; + for (whowas_set::iterator j = set->begin(); j != set->end(); ++j) + delete *j; + + delete set; + } +} + +WhoWasGroup::WhoWasGroup(User* user) : host(user->host), dhost(user->dhost), ident(user->ident), + server(user->server->GetName()), gecos(user->fullname), signon(user->signon) +{ +} + +class ModuleWhoWas : public Module +{ + CommandWhowas cmd; + + public: + ModuleWhoWas() : cmd(this) + { + } + + void OnGarbageCollect() + { + // Remove all entries older than MaxKeep + cmd.Maintain(); + } + + void OnUserQuit(User* user, const std::string& message, const std::string& oper_message) + { + cmd.AddToWhoWas(user); + } + + ModResult OnStats(char symbol, User* user, string_list &results) + { + if (symbol == 'z') + results.push_back("249 "+user->nick+" :"+cmd.GetStats()); + + 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); + + if ((NewGroupSize == cmd.GroupSize) && (NewMaxGroups == cmd.MaxGroups) && (NewMaxKeep == cmd.MaxKeep)) + return; + + cmd.GroupSize = NewGroupSize; + cmd.MaxGroups = NewMaxGroups; + cmd.MaxKeep = NewMaxKeep; + cmd.Prune(); + } + + Version GetVersion() + { + return Version("WHOWAS", VF_VENDOR); + } +}; + +MODULE_INIT(ModuleWhoWas) diff --git a/src/commands/cmd_eline.cpp b/src/coremods/core_xline/cmd_eline.cpp index ca39f9061..26b49894b 100644 --- a/src/commands/cmd_eline.cpp +++ b/src/coremods/core_xline/cmd_eline.cpp @@ -20,26 +20,14 @@ #include "inspircd.h" #include "xline.h" +#include "core_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 +CommandEline::CommandEline(Module* parent) + : Command(parent, "ELINE", 1, 3) { - 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); -}; + flags_needed = 'o'; + syntax = "<ident@host> [<duration> :<reason>]"; +} /** Handle /ELINE */ @@ -60,17 +48,17 @@ CmdResult CommandEline::Handle (const std::vector<std::string>& parameters, User 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)) + if (ih.first.empty()) + { + user->WriteNotice("*** Target not found"); return CMD_FAILURE; + } - long duration = ServerInstance->Duration(parameters[1].c_str()); + 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)) { @@ -81,7 +69,7 @@ CmdResult CommandEline::Handle (const std::vector<std::string>& parameters, 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->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()); } @@ -89,7 +77,7 @@ CmdResult CommandEline::Handle (const std::vector<std::string>& parameters, User else { delete el; - user->WriteServ("NOTICE %s :*** E-Line for %s already exists",user->nick.c_str(),target.c_str()); + user->WriteNotice("*** E-Line for " + target + " already exists"); } } else @@ -100,11 +88,9 @@ CmdResult CommandEline::Handle (const std::vector<std::string>& parameters, User } else { - user->WriteServ("NOTICE %s :*** E-Line %s not found in list, try /stats e.",user->nick.c_str(),target.c_str()); + user->WriteNotice("*** E-Line " + target + " not found in list, try /stats e"); } } return CMD_SUCCESS; } - -COMMAND_INIT(CommandEline) diff --git a/src/commands/cmd_gline.cpp b/src/coremods/core_xline/cmd_gline.cpp index 6505b7464..3f042c366 100644 --- a/src/commands/cmd_gline.cpp +++ b/src/coremods/core_xline/cmd_gline.cpp @@ -20,27 +20,15 @@ #include "inspircd.h" #include "xline.h" +#include "core_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 +CommandGline::CommandGline(Module* parent) + : Command(parent, "GLINE", 1, 3) { - 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); -}; - + flags_needed = 'o'; + Penalty = 0; + syntax = "<ident@host> [<duration> :<reason>]"; +} /** Handle /GLINE */ @@ -63,20 +51,21 @@ CmdResult CommandGline::Handle (const std::vector<std::string>& parameters, User if (ih.first.empty()) { - user->WriteServ("NOTICE %s :*** Target not found", user->nick.c_str()); + user->WriteNotice("*** Target not found"); return CMD_FAILURE; } - if (ServerInstance->HostMatchesEveryone(ih.first+"@"+ih.second,user)) + 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->WriteServ("NOTICE %s :*** G-Line cannot operate on nick!user@host masks",user->nick.c_str()); + user->WriteNotice("*** G-Line cannot operate on nick!user@host masks"); return CMD_FAILURE; } - long duration = ServerInstance->Duration(parameters[1].c_str()); + 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)) { @@ -87,7 +76,7 @@ CmdResult CommandGline::Handle (const std::vector<std::string>& parameters, 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->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()); } @@ -97,7 +86,7 @@ CmdResult CommandGline::Handle (const std::vector<std::string>& parameters, User else { delete gl; - user->WriteServ("NOTICE %s :*** G-Line for %s already exists",user->nick.c_str(),target.c_str()); + user->WriteNotice("** G-Line for " + target + " already exists"); } } @@ -109,11 +98,9 @@ CmdResult CommandGline::Handle (const std::vector<std::string>& parameters, User } else { - user->WriteServ("NOTICE %s :*** G-line %s not found in list, try /stats g.",user->nick.c_str(),target.c_str()); + user->WriteNotice("*** G-Line " + target + " not found in list, try /stats g."); } } return CMD_SUCCESS; } - -COMMAND_INIT(CommandGline) diff --git a/src/commands/cmd_kline.cpp b/src/coremods/core_xline/cmd_kline.cpp index ce3642f91..50ab88398 100644 --- a/src/commands/cmd_kline.cpp +++ b/src/coremods/core_xline/cmd_kline.cpp @@ -20,27 +20,15 @@ #include "inspircd.h" #include "xline.h" +#include "core_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 +CommandKline::CommandKline(Module* parent) + : Command(parent, "KLINE", 1, 3) { - 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); -}; - + flags_needed = 'o'; + Penalty = 0; + syntax = "<ident@host> [<duration> :<reason>]"; +} /** Handle /KLINE */ @@ -63,20 +51,21 @@ CmdResult CommandKline::Handle (const std::vector<std::string>& parameters, User if (ih.first.empty()) { - user->WriteServ("NOTICE %s :*** Target not found", user->nick.c_str()); + user->WriteNotice("*** Target not found"); return CMD_FAILURE; } - if (ServerInstance->HostMatchesEveryone(ih.first+"@"+ih.second,user)) + InsaneBan::IPHostMatcher matcher; + if (InsaneBan::MatchesEveryone(ih.first+"@"+ih.second, matcher, user, "K", "hostmasks")) 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()); + user->WriteNotice("*** K-Line cannot operate on nick!user@host masks"); return CMD_FAILURE; } - long duration = ServerInstance->Duration(parameters[1].c_str()); + 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)) { @@ -87,7 +76,7 @@ CmdResult CommandKline::Handle (const std::vector<std::string>& parameters, 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->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()); } @@ -97,7 +86,7 @@ CmdResult CommandKline::Handle (const std::vector<std::string>& parameters, User else { delete kl; - user->WriteServ("NOTICE %s :*** K-Line for %s already exists",user->nick.c_str(),target.c_str()); + user->WriteNotice("*** K-Line for " + target + " already exists"); } } else @@ -108,11 +97,9 @@ CmdResult CommandKline::Handle (const std::vector<std::string>& parameters, User } else { - user->WriteServ("NOTICE %s :*** K-Line %s not found in list, try /stats k.",user->nick.c_str(),target.c_str()); + user->WriteNotice("*** K-Line " + target + " not found in list, try /stats k."); } } return CMD_SUCCESS; } - -COMMAND_INIT(CommandKline) diff --git a/src/commands/cmd_qline.cpp b/src/coremods/core_xline/cmd_qline.cpp index 3118798e6..955efeaf0 100644 --- a/src/commands/cmd_qline.cpp +++ b/src/coremods/core_xline/cmd_qline.cpp @@ -21,38 +21,31 @@ #include "inspircd.h" #include "xline.h" +#include "core_xline.h" -/** Handle /QLINE. */ -class CommandQline : public Command +CommandQline::CommandQline(Module* parent) + : Command(parent, "QLINE", 1, 3) { - 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); -}; - + flags_needed = 'o'; + Penalty = 0; + syntax = "<nick> [<duration> :<reason>]"; +} CmdResult CommandQline::Handle (const std::vector<std::string>& parameters, User *user) { if (parameters.size() >= 3) { - if (ServerInstance->NickMatchesEveryone(parameters[0],user)) + 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->WriteServ("NOTICE %s :*** A Q-Line only bans a nick pattern, not a nick!user@host pattern.",user->nick.c_str()); + user->WriteNotice("*** A Q-Line only bans a nick pattern, not a nick!user@host pattern."); return CMD_FAILURE; } - long duration = ServerInstance->Duration(parameters[1].c_str()); + 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)) { @@ -63,7 +56,7 @@ CmdResult CommandQline::Handle (const std::vector<std::string>& parameters, 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->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()); } @@ -72,7 +65,7 @@ CmdResult CommandQline::Handle (const std::vector<std::string>& parameters, User else { delete ql; - user->WriteServ("NOTICE %s :*** Q-Line for %s already exists",user->nick.c_str(),parameters[0].c_str()); + user->WriteNotice("*** Q-Line for " + parameters[0] + " already exists"); } } else @@ -83,7 +76,7 @@ CmdResult CommandQline::Handle (const std::vector<std::string>& parameters, User } else { - user->WriteServ("NOTICE %s :*** Q-Line %s not found in list, try /stats q.",user->nick.c_str(),parameters[0].c_str()); + user->WriteNotice("*** Q-Line " + parameters[0] + " not found in list, try /stats q."); return CMD_FAILURE; } } @@ -91,5 +84,7 @@ CmdResult CommandQline::Handle (const std::vector<std::string>& parameters, User return CMD_SUCCESS; } - -COMMAND_INIT(CommandQline) +bool CommandQline::NickMatcher::Check(User* user, const std::string& nick) const +{ + return InspIRCd::Match(user->nick, nick); +} diff --git a/src/commands/cmd_zline.cpp b/src/coremods/core_xline/cmd_zline.cpp index 91d9c6255..859be1004 100644 --- a/src/commands/cmd_zline.cpp +++ b/src/coremods/core_xline/cmd_zline.cpp @@ -21,25 +21,15 @@ #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 +#include "core_xline.h" + +CommandZline::CommandZline(Module* parent) + : Command(parent, "ZLINE", 1, 3) { - 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); -}; + flags_needed = 'o'; + Penalty = 0; + syntax = "<ipmask> [<duration> :<reason>]"; +} CmdResult CommandZline::Handle (const std::vector<std::string>& parameters, User *user) { @@ -49,7 +39,7 @@ CmdResult CommandZline::Handle (const std::vector<std::string>& parameters, User { 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()); + user->WriteNotice("*** You cannot include a nickname in a zline, a zline must ban only an IP mask"); return CMD_FAILURE; } @@ -69,11 +59,11 @@ CmdResult CommandZline::Handle (const std::vector<std::string>& parameters, User ipaddr++; } - if (ServerInstance->IPMatchesEveryone(ipaddr,user)) + IPMatcher matcher; + if (InsaneBan::MatchesEveryone(ipaddr, matcher, user, "Z", "ipmasks")) return CMD_FAILURE; - long duration = ServerInstance->Duration(parameters[1].c_str()); - + 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)) { @@ -84,7 +74,7 @@ CmdResult CommandZline::Handle (const std::vector<std::string>& parameters, 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->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()); } @@ -93,7 +83,7 @@ CmdResult CommandZline::Handle (const std::vector<std::string>& parameters, User else { delete zl; - user->WriteServ("NOTICE %s :*** Z-Line for %s already exists",user->nick.c_str(),ipaddr); + user->WriteNotice("*** Z-Line for " + std::string(ipaddr) + " already exists"); } } else @@ -104,7 +94,7 @@ CmdResult CommandZline::Handle (const std::vector<std::string>& parameters, User } else { - user->WriteServ("NOTICE %s :*** Z-Line %s not found in list, try /stats Z.",user->nick.c_str(),target.c_str()); + user->WriteNotice("*** Z-Line " + target + " not found in list, try /stats Z."); return CMD_FAILURE; } } @@ -112,4 +102,7 @@ CmdResult CommandZline::Handle (const std::vector<std::string>& parameters, User return CMD_SUCCESS; } -COMMAND_INIT(CommandZline) +bool CommandZline::IPMatcher::Check(User* user, const std::string& ip) const +{ + return InspIRCd::Match(user->GetIPString(), ip, ascii_case_insensitive_map); +} diff --git a/src/coremods/core_xline/core_xline.cpp b/src/coremods/core_xline/core_xline.cpp new file mode 100644 index 000000000..7daa70b49 --- /dev/null +++ b/src/coremods/core_xline/core_xline.cpp @@ -0,0 +1,72 @@ +/* + * 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_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) + { + } + + Version GetVersion() CXX11_OVERRIDE + { + return Version("Provides the ELINE, GLINE, KLINE, QLINE, and ZLINE commands", VF_VENDOR|VF_CORE); + } +}; + +MODULE_INIT(CoreModXLine) diff --git a/src/coremods/core_xline/core_xline.h b/src/coremods/core_xline/core_xline.h new file mode 100644 index 000000000..d4ad498a0 --- /dev/null +++ b/src/coremods/core_xline/core_xline.h @@ -0,0 +1,164 @@ +/* + * 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); +}; diff --git a/src/cull_list.cpp b/src/cull_list.cpp index 956ed3494..5cbe3aef3 100644 --- a/src/cull_list.cpp +++ b/src/cull_list.cpp @@ -46,14 +46,14 @@ void CullList::Apply() 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); } } @@ -65,7 +65,7 @@ void CullList::Apply() } 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(); } } diff --git a/src/dns.cpp b/src/dns.cpp deleted file mode 100644 index 63bde0ecc..000000000 --- a/src/dns.cpp +++ /dev/null @@ -1,1110 +0,0 @@ -/* - * 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, unsigned int ttl) : data(res) -{ - 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: - { - inet_ntop(AF_INET6, data.first, formatted, sizeof(formatted)); - char* c = strstr(formatted,":0:"); - if (c != NULL) - { - memmove(c+1,c+2,strlen(c+2) + 1); - c += 2; - while (memcmp(c,"0:",2) == 0) - memmove(c,c+2,strlen(c+2) + 1); - if (memcmp(c,"0",2) == 0) - *c = 0; - if (memcmp(formatted,"0::",3) == 0) - memmove(formatted,formatted + 1, strlen(formatted + 1) + 1); - } - 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; - delete req; - return DNSResult(this_id,resultstr,ttl,ro); - } -} - -/** 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 - { - cached = true; - return; - } - } - - 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.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; - } - } - } -} diff --git a/src/filelogger.cpp b/src/filelogger.cpp index 0575256d0..fff0b37fa 100644 --- a/src/filelogger.cpp +++ b/src/filelogger.cpp @@ -19,15 +19,12 @@ */ -/* $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); } @@ -38,9 +35,9 @@ FileLogStream::~FileLogStream() 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) @@ -53,11 +50,9 @@ void FileLogStream::OnLog(int loglevel, const std::string &type, const std::stri time_t local = ServerInstance->Time(); struct tm *timeinfo = localtime(&local); - strlcpy(TIMESTR,asctime(timeinfo),26); - TIMESTR[24] = ':'; + TIMESTR.assign(asctime(timeinfo), 24); LAST = ServerInstance->Time(); } - std::string out = std::string(TIMESTR) + " " + text.c_str() + "\n"; - this->f->WriteLogLine(out); + this->f->WriteLogLine(TIMESTR + " " + type + ": " + text + "\n"); } diff --git a/src/fileutils.cpp b/src/fileutils.cpp new file mode 100644 index 000000000..731e4ea01 --- /dev/null +++ b/src/fileutils.cpp @@ -0,0 +1,102 @@ +/* + * 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] == ':'); +} diff --git a/src/hashcomp.cpp b/src/hashcomp.cpp index 3fb7f84fb..32f74475f 100644 --- a/src/hashcomp.cpp +++ b/src/hashcomp.cpp @@ -20,11 +20,8 @@ */ -/* $Core */ - #include "inspircd.h" #include "hashcomp.h" -#include "hash_map.h" /****************************************************** * @@ -35,7 +32,7 @@ * 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 @@ -51,101 +48,109 @@ * ******************************************************/ -/** 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 { @@ -165,6 +170,39 @@ bool irc::StrHashComp::operator()(const std::string& s1, const std::string& s2) 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 @@ -217,60 +255,30 @@ const char* irc::irc_char_traits::find(const char* s1, int n, char c) 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++; + bool first = !pos; - 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; - } - - 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) @@ -297,85 +305,50 @@ bool irc::tokenstream::GetToken(long &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; + if (this->StreamEnd()) + { + token.clear(); + return false; + } - while (n != tokens.end()) + if (!this->allow_empty) { - if ((*n == sep) || (n+1 == tokens.end())) + this->pos = this->tokens.find_first_not_of(this->sep, this->pos); + if (this->pos == std::string::npos) { - 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; + this->pos = this->tokens.length() + 1; + token.clear(); + return false; } - - n++; } - token.clear(); - return false; -} - -const std::string irc::sepstream::GetRemaining() -{ - return std::string(n, tokens.end()); -} + size_t p = this->tokens.find(this->sep, this->pos); + if (p == std::string::npos) + p = this->tokens.length(); -bool irc::sepstream::StreamEnd() -{ - return ((n + 1) == tokens.end()); -} + token = this->tokens.substr(this->pos, p - this->pos); + this->pos = p + 1; -irc::sepstream::~sepstream() -{ + return true; } -std::string irc::hex(const unsigned char *raw, size_t rawsz) +const std::string irc::sepstream::GetRemaining() { - 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; + return !this->StreamEnd() ? this->tokens.substr(this->pos) : ""; } -CoreExport const char* irc::Spacify(const char* n) +bool irc::sepstream::StreamEnd() { - static char x[MAXBUF]; - strlcpy(x,n,MAXBUF); - for (char* y = x; *y; y++) - if (*y == '_') - *y = ' '; - return x; + return this->pos > this->tokens.length(); } - irc::modestacker::modestacker(bool add) : adding(add) { sequence.clear(); @@ -441,38 +414,15 @@ int irc::modestacker::GetStackedLine(std::vector<std::string> &result, int max_l return n; } -irc::stringjoiner::stringjoiner(const std::string &seperator, const std::vector<std::string> &sequence, int begin, int end) +std::string irc::stringjoiner(const std::vector<std::string>& sequence, char separator) { - 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]); -} - -irc::stringjoiner::stringjoiner(const std::string &seperator, const std::deque<std::string> &sequence, int begin, int end) -{ - 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]); -} - -irc::stringjoiner::stringjoiner(const std::string &seperator, const char* const* sequence, int begin, int end) -{ - 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]); -} + std::string joined; + if (sequence.empty()) + return joined; // nothing to do here -std::string& irc::stringjoiner::GetJoined() -{ + 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; } @@ -549,25 +499,3 @@ long irc::portparser::GetToken() 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; -}*/ diff --git a/src/helperfuncs.cpp b/src/helperfuncs.cpp index 439320c1e..bebb22007 100644 --- a/src/helperfuncs.cpp +++ b/src/helperfuncs.cpp @@ -22,8 +22,6 @@ */ -/* $Core */ - #ifdef _WIN32 #define _CRT_RAND_S #include <stdlib.h> @@ -34,66 +32,26 @@ #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); + 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); - - if (iter == this->Users->clientlist->end()) + if (iter == this->Users->clientlist.end()) return NULL; return iter->second; @@ -101,36 +59,20 @@ User* InspIRCd::FindNickOnly(const char* nick) 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; @@ -145,8 +87,8 @@ void InspIRCd::SendError(const std::string &s) 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 */ @@ -155,12 +97,6 @@ void InspIRCd::SendError(const std::string &s) } } -/* return channel count */ -long InspIRCd::ChannelCount() -{ - return chanlist->size(); -} - bool InspIRCd::IsValidMask(const std::string &mask) { const char* dest = mask.c_str(); @@ -281,47 +217,35 @@ void InspIRCd::ProcessColors(file_cache& input) } /* 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 <= '}')) { @@ -329,7 +253,7 @@ bool IsNickHandler::Call(const char* n, size_t max) 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; @@ -339,17 +263,16 @@ bool IsNickHandler::Call(const char* n, size_t max) 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 <= '}')) { @@ -367,7 +290,7 @@ bool IsIdentHandler::Call(const char* n) 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 @@ -377,35 +300,13 @@ bool IsSIDHandler::Call(const std::string &str) ((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 @@ -419,24 +320,20 @@ void InspIRCd::SendWhoisLine(User* user, User* dest, int numeric, const std::str 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; @@ -476,21 +373,34 @@ long InspIRCd::Duration(const std::string &str) 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; + } + + formatBuffer.resize(formatBuffer.size() * 2); + } - return (Config->ulines.find(sserver.c_str()) != Config->ulines.end()); + 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) @@ -517,26 +427,6 @@ std::string InspIRCd::TimeString(time_t curtime) return std::string(asctime(timeinfo),24); } -// 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=,"); - - 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); - } -} - std::string InspIRCd::GenRandomStr(int length, bool printable) { char* buf = new char[length]; @@ -592,7 +482,7 @@ ModResult OnCheckExemptionHandler::Call(User* user, Channel* chan, const std::st 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 == '*') diff --git a/src/inspircd.cpp b/src/inspircd.cpp index 766aeaf8e..24072868a 100644 --- a/src/inspircd.cpp +++ b/src/inspircd.cpp @@ -26,9 +26,7 @@ */ -/* $Core */ #include "inspircd.h" -#include "inspircd_version.h" #include <signal.h> #ifndef _WIN32 @@ -78,28 +76,26 @@ unsigned const char *national_case_insensitive_map = rfc_case_insensitive_map; */ 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; @@ -109,21 +105,18 @@ template<typename T> static void DeleteZero(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"); - } + LocalUserList& list = Users->local_users; + for (LocalUserList::iterator i = list.begin(); i != list.end(); ++i) + Users->QuitUser(*i, "Server shutdown"); GlobalCulls.Apply(); Modules->UnloadAll(); @@ -131,9 +124,10 @@ void InspIRCd::Cleanup() /* 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); @@ -144,87 +138,13 @@ void InspIRCd::Cleanup() 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(); + SocketEngine::Deinit(); + 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(); - } -} - void InspIRCd::SetSignals() { #ifndef _WIN32 @@ -258,8 +178,8 @@ bool InspIRCd::DaemonSeed() // 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) { @@ -282,13 +202,13 @@ bool InspIRCd::DaemonSeed() 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 @@ -299,7 +219,7 @@ void InspIRCd::WritePID(const std::string &filename) #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()) { @@ -309,7 +229,7 @@ void InspIRCd::WritePID(const std::string &filename) 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 @@ -323,43 +243,35 @@ InspIRCd::InspIRCd(int argc, char** argv) : * 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; @@ -369,28 +281,22 @@ InspIRCd::InspIRCd(int argc, char** argv) : // This must be created first, so other parts of Insp can use it while starting up this->Logs = new LogManager; - SE = CreateSocketEngine(); + SocketEngine::Init(); 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(); - this->Config = new ServerConfig; this->SNO = new SnomaskManager; this->BanCache = new BanCacheManager; this->Modules = new ModuleManager(); + dynamic_reference_base::reset_all(); this->stats = new serverstats(); - this->Timers = new TimerManager; this->Parser = new CommandParser; this->XLines = new XLineManager; @@ -420,28 +326,26 @@ InspIRCd::InspIRCd(int argc, char** 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 */ @@ -451,19 +355,21 @@ InspIRCd::InspIRCd(int argc, char** argv) : 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 << VERSION << " " << REVISION << std::endl; Exit(EXIT_STATUS_NOERROR); } @@ -477,28 +383,22 @@ InspIRCd::InspIRCd(int argc, char** argv) : 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; } @@ -506,7 +406,7 @@ InspIRCd::InspIRCd(int argc, char** argv) : #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); } } @@ -515,7 +415,7 @@ InspIRCd::InspIRCd(int argc, char** argv) : 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;
+ 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; @@ -526,14 +426,14 @@ InspIRCd::InspIRCd(int argc, char** argv) : this->CheckRoot(); else { - 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;
+ 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 @@ -545,47 +445,32 @@ InspIRCd::InspIRCd(int argc, char** argv) : 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]; - - 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); + Config->sid = UIDGenerator::GenerateSID(Config->ServerName, Config->ServerDesc); - Config->sid = sidstr; - } + // Initialize the UID generator with our sid + this->UIDGen.init(Config->sid); - /* 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); @@ -594,8 +479,8 @@ InspIRCd::InspIRCd(int argc, char** argv) : 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()) @@ -612,7 +497,7 @@ InspIRCd::InspIRCd(int argc, char** argv) : 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) @@ -620,7 +505,7 @@ InspIRCd::InspIRCd(int argc, char** argv) : 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)); } } @@ -634,7 +519,7 @@ InspIRCd::InspIRCd(int argc, char** argv) : * * -- nenolod */ - if ((!do_nofork) && (!do_testsuite) && (!Config->cmdline.forcedebug)) + if ((!do_nofork) && (!Config->cmdline.forcedebug)) { int fd = open("/dev/null", O_RDWR); @@ -643,16 +528,16 @@ InspIRCd::InspIRCd(int argc, char** argv) : 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 */ @@ -667,7 +552,7 @@ InspIRCd::InspIRCd(int argc, char** argv) : 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"); @@ -681,7 +566,7 @@ InspIRCd::InspIRCd(int argc, char** argv) : 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); } @@ -693,7 +578,7 @@ InspIRCd::InspIRCd(int argc, char** argv) : 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); } @@ -701,7 +586,7 @@ InspIRCd::InspIRCd(int argc, char** argv) : 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); } } @@ -716,7 +601,7 @@ InspIRCd::InspIRCd(int argc, char** argv) : 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); } @@ -724,7 +609,7 @@ InspIRCd::InspIRCd(int argc, char** argv) : 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); } } @@ -753,15 +638,17 @@ void InspIRCd::UpdateTime() #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; @@ -776,7 +663,7 @@ int InspIRCd::Run() 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(); @@ -820,21 +707,21 @@ int InspIRCd::Run() { SNO->WriteToSnoMask('d', "\002EH?!\002 -- Time is jumping FORWARDS! Clock skipped %lu secs.", (unsigned long)TIME.tv_sec - OLDTIME); } -
+ 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(); } } @@ -846,8 +733,8 @@ int InspIRCd::Run() * 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(); @@ -859,25 +746,6 @@ int InspIRCd::Run() 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; diff --git a/src/inspsocket.cpp b/src/inspsocket.cpp index 356904f74..f22645168 100644 --- a/src/inspsocket.cpp +++ b/src/inspsocket.cpp @@ -26,6 +26,7 @@ #include "socket.h" #include "inspstring.h" #include "socketengine.h" +#include "iohook.h" #ifndef DISABLE_WRITEV #include <sys/uio.h> @@ -47,7 +48,7 @@ BufferedSocket::BufferedSocket(int newfd) 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) @@ -66,7 +67,7 @@ BufferedSocketError BufferedSocket::BeginConnect(const std::string &ipaddr, int 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; } @@ -92,13 +93,13 @@ BufferedSocketError BufferedSocket::BeginConnect(const irc::sockets::sockaddrs& 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; @@ -106,13 +107,13 @@ BufferedSocketError BufferedSocket::BeginConnect(const irc::sockets::sockaddrs& 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); + 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; } @@ -122,23 +123,22 @@ void StreamSocket::Close() { // 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); } } @@ -161,17 +161,17 @@ bool StreamSocket::GetNextLine(std::string& line, char delim) 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) @@ -182,36 +182,36 @@ void StreamSocket::DoRead() 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); } } } @@ -225,12 +225,12 @@ void StreamSocket::DoWrite() 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; @@ -258,9 +258,9 @@ void StreamSocket::DoWrite() } 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 @@ -285,7 +285,7 @@ void StreamSocket::DoWrite() #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"); @@ -294,14 +294,14 @@ void StreamSocket::DoWrite() 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; @@ -311,7 +311,7 @@ void StreamSocket::DoWrite() 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 @@ -319,8 +319,8 @@ void StreamSocket::DoWrite() } 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 @@ -407,11 +407,11 @@ void StreamSocket::DoWrite() 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 @@ -421,7 +421,7 @@ void StreamSocket::WriteData(const std::string &data) { 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; } @@ -430,15 +430,18 @@ void StreamSocket::WriteData(const std::string &data) 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) { @@ -454,6 +457,8 @@ void SocketTimeout::Tick(time_t) } this->sock->Timeout = NULL; + delete this; + return false; } void BufferedSocket::OnConnected() { } @@ -465,10 +470,8 @@ void BufferedSocket::DoWrite() { 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(); } @@ -478,8 +481,8 @@ BufferedSocket::~BufferedSocket() this->Close(); if (Timeout) { - ServerInstance->Timers->DelTimer(Timeout); - Timeout = NULL; + // The timer is removed from the TimerManager in Timer::~Timer() + delete Timeout; } } @@ -530,14 +533,13 @@ void StreamSocket::HandleEvent(EventType et, int errornum) } 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); } } - diff --git a/src/inspstring.cpp b/src/inspstring.cpp index 534b7ce00..7fa4762c5 100644 --- a/src/inspstring.cpp +++ b/src/inspstring.cpp @@ -21,140 +21,18 @@ #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; } diff --git a/src/listensocket.cpp b/src/listensocket.cpp index ae11c3b48..e73a8f4ce 100644 --- a/src/listensocket.cpp +++ b/src/listensocket.cpp @@ -22,11 +22,16 @@ #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); @@ -51,23 +56,38 @@ ListenSocket::ListenSocket(ConfigTag* tag, const irc::sockets::sockaddrs& bind_t } #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(); } } @@ -75,12 +95,10 @@ ListenSocket::~ListenSocket() { 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)); } } @@ -91,9 +109,9 @@ void ListenSocket::AcceptInternal() 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++; @@ -103,7 +121,7 @@ void ListenSocket::AcceptInternal() 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); } @@ -117,11 +135,11 @@ void ListenSocket::AcceptInternal() * 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()) + if (incomingSockfd >= SocketEngine::GetMaxFds()) { - ServerInstance->Logs->Log("SOCKET", DEBUG, "Server is full"); - ServerInstance->SE->Shutdown(incomingSockfd, 2); - ServerInstance->SE->Close(incomingSockfd); + ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Server is full"); + SocketEngine::Shutdown(incomingSockfd, 2); + SocketEngine::Close(incomingSockfd); ServerInstance->stats->statsRefused++; return; } @@ -156,7 +174,7 @@ void ListenSocket::AcceptInternal() } } - ServerInstance->SE->NonBlocking(incomingSockfd); + SocketEngine::NonBlocking(incomingSockfd); ModResult res; FIRST_MOD_RESULT(OnAcceptConnection, res, (incomingSockfd, this, &client, &server)); @@ -176,9 +194,9 @@ void ListenSocket::AcceptInternal() else { ServerInstance->stats->statsRefused++; - ServerInstance->Logs->Log("SOCKET",DEFAULT,"Refusing connection on %s - %s", + 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); } } @@ -187,13 +205,26 @@ void ListenSocket::HandleEvent(EventType e, int err) 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); +} diff --git a/src/listmode.cpp b/src/listmode.cpp new file mode 100644 index 000000000..0f139bb01 --- /dev/null +++ b/src/listmode.cpp @@ -0,0 +1,222 @@ +/* + * 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, irc::modestacker& stack) +{ + ChanData* cd = extItem.get(channel); + if (cd) + { + for (ModeList::iterator it = cd->list.begin(); it != cd->list.end(); it++) + { + stack.Push(this->GetModeChar(), 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); + } + + if (chanlimits.empty()) + 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&) +{ +} diff --git a/src/logger.cpp b/src/logger.cpp index 89b2be019..61ac517c7 100644 --- a/src/logger.cpp +++ b/src/logger.cpp @@ -21,7 +21,6 @@ #include "inspircd.h" - #include "filelogger.h" /* @@ -51,9 +50,13 @@ * */ +const char LogStream::LogHeader[] = + "Log started for " VERSION " (" REVISION ", " MODULE_INIT_STR ")" + " - compiled on " SYSTEM; + LogManager::LogManager() + : Logging(false) { - Logging = false; } LogManager::~LogManager() @@ -82,41 +85,41 @@ void LogManager::OpenFileLogs() } 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)); @@ -126,7 +129,7 @@ void LogManager::OpenFileLogs() 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); } } @@ -135,13 +138,16 @@ void LogManager::CloseLogs() { 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) @@ -187,36 +193,13 @@ void LogManager::AddLogTypes(const std::string &types, LogStream* l, bool autocl 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; } @@ -228,21 +211,18 @@ void LogManager::DelLogStream(LogStream* l) 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); } @@ -252,8 +232,7 @@ bool LogManager::DelLogType(const std::string &type, LogStream *l) 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()) @@ -293,24 +272,17 @@ bool LogManager::DelLogType(const std::string &type, LogStream *l) 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) { @@ -354,8 +326,8 @@ void FileWriter::WriteLogLine(const std::string &line) // 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); } diff --git a/src/mode.cpp b/src/mode.cpp index 2a32dfac2..9d24160f6 100644 --- a/src/mode.cpp +++ b/src/mode.cpp @@ -24,35 +24,12 @@ #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), m_paramtype(TR_TEXT), + parameters_taken(Params), mode(modeletter), oper(false), + list(false), m_type(type), type_id(mclass), levelrequired(HALFOP_VALUE) { } @@ -67,16 +44,6 @@ ModeHandler::~ModeHandler() { } -bool ModeHandler::IsListMode() -{ - return list; -} - -unsigned int ModeHandler::GetPrefixRank() -{ - return 0; -} - int ModeHandler::GetNumParams(bool adding) { switch (parameters_taken) @@ -127,7 +94,7 @@ ModeAction SimpleUserModeHandler::OnModeChange(User* source, User* dest, Channel { /* 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 @@ -136,7 +103,7 @@ ModeAction SimpleUserModeHandler::OnModeChange(User* source, User* dest, Channel 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; } @@ -146,7 +113,7 @@ ModeAction SimpleChannelModeHandler::OnModeChange(User* source, User* dest, Chan { /* 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 @@ -155,42 +122,20 @@ ModeAction SimpleChannelModeHandler::OnModeChange(User* source, User* dest, Chan 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() @@ -198,29 +143,13 @@ 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) -{ -} - -User* ModeParser::SanityChecks(User *user, const char *dest, Channel *chan, int) +void ModeWatcher::AfterMode(User*, User*, Channel*, const std::string&, bool) { - User *d; - if ((!user) || (!dest) || (!chan) || (!*dest)) - { - return NULL; - } - d = ServerInstance->FindNick(dest); - if (!d) - { - user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(), dest); - return NULL; - } - return d; } void ModeParser::DisplayCurrentModes(User *user, User* targetuser, Channel* targetchannel, const char* text) @@ -228,8 +157,8 @@ void ModeParser::DisplayCurrentModes(User *user, User* targetuser, Channel* targ if (targetchannel) { /* 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); + 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); return; } else @@ -237,24 +166,82 @@ void ModeParser::DisplayCurrentModes(User *user, User* targetuser, Channel* targ 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()); + user->WriteNumeric(RPL_UMODEIS, ":+%s", targetuser->FormatModes()); + if ((targetuser->IsOper())) + { + ModeHandler* snomask = FindMode('s', MODETYPE_USER); + user->WriteNumeric(RPL_SNOMASKIS, "%s :Server notice mask", snomask->GetUserParameter(user).c_str()); + } return; } else { - user->WriteNumeric(ERR_USERSDONTMATCH, "%s :Can't view modes for other users", user->nick.c_str()); + user->WriteNumeric(ERR_USERSDONTMATCH, ":Can't view modes for other users"); return; } } } +PrefixMode::PrefixMode(Module* Creator, const std::string& Name, char ModeLetter) + : ModeHandler(Creator, Name, ModeLetter, PARAM_ALWAYS, MODETYPE_CHANNEL, MC_PREFIX) + , prefix(0), prefixrank(0) +{ + list = true; + m_paramtype = TR_NICK; +} + +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) + { + source->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameter.c_str()); + return MODEACTION_DENY; + } + + Membership* memb = chan->GetUser(target); + if (!memb) + return MODEACTION_DENY; + + parameter = target->nick; + return (memb->SetPrefix(this, adding) ? MODEACTION_ALLOW : MODEACTION_DENY); +} + +ModeAction ParamModeBase::OnModeChange(User* source, User*, Channel* chan, std::string& parameter, bool adding) +{ + if (adding) + { + 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 (!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) { ModeType type = chan ? MODETYPE_CHANNEL : MODETYPE_USER; - unsigned char mask = chan ? MASK_CHANNEL : MASK_USER; ModeHandler *mh = FindMode(modechar, type); int pcnt = mh->GetNumParams(adding); @@ -264,7 +251,7 @@ ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, bool 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; @@ -286,10 +273,10 @@ ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, bool 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 @@ -298,34 +285,39 @@ ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, bool } } 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; } } @@ -333,43 +325,19 @@ ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, bool 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); @@ -379,13 +347,18 @@ ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, bool 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::Process(const std::vector<std::string>& parameters, User* user, ModeProcessFlag flags) { const std::string& target = parameters[0]; Channel* targetchannel = ServerInstance->FindChan(target); @@ -405,7 +378,7 @@ void ModeParser::Process(const std::vector<std::string>& parameters, User *user, if ((!targetchannel) && ((!targetuser) || (IS_SERVER(targetuser)))) { - user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(),target.c_str()); + user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", target.c_str()); return; } if (parameters.size() == 1) @@ -419,14 +392,14 @@ void ModeParser::Process(const std::vector<std::string>& parameters, User *user, bool SkipAccessChecks = false; - if (!IS_LOCAL(user) || ServerInstance->ULine(user->server) || MOD_RESULT == MOD_RES_ALLOW) + if (!IS_LOCAL(user) || MOD_RESULT == MOD_RES_ALLOW) SkipAccessChecks = true; else if (MOD_RESULT == MOD_RES_DENY) return; if (targetuser && !SkipAccessChecks && user != targetuser) { - user->WriteNumeric(ERR_USERSDONTMATCH, "%s :Can't change mode for other users", user->nick.c_str()); + user->WriteNumeric(ERR_USERSDONTMATCH, ":Can't change mode for other users"); return; } @@ -454,7 +427,7 @@ void ModeParser::Process(const std::vector<std::string>& parameters, User *user, 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; } @@ -472,9 +445,9 @@ void ModeParser::Process(const std::vector<std::string>& parameters, User *user, /* Make sure the user isn't trying to slip in an invalid parameter */ if ((parameter.find(':') == 0) || (parameter.rfind(' ') != std::string::npos)) continue; - if (merge && targetchannel && targetchannel->IsModeSet(modechar) && !mh->IsListMode()) + if ((flags & MODE_MERGE) && targetchannel && targetchannel->IsModeSet(mh) && !mh->IsListMode()) { - std::string ours = targetchannel->GetModeParameter(modechar); + std::string ours = targetchannel->GetModeParameter(mh); if (!mh->ResolveModeConflict(parameter, ours, targetchannel)) /* we won the mode merge, don't apply this mode */ continue; @@ -496,16 +469,9 @@ void ModeParser::Process(const std::vector<std::string>& parameters, User *user, if (pcnt) { - 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); + LastParseTranslate.push_back(mh->GetTranslateType()); } if ( (output_mode.length() + output_parameters.str().length() > 450) @@ -526,16 +492,15 @@ void ModeParser::Process(const std::vector<std::string>& parameters, User *user, LastParse.append(output_mode); LastParse.append(output_parameters.str()); + if (!(flags & MODE_LOCALONLY)) + ServerInstance->PI->SendMode(user, targetuser, targetchannel, LastParseParams, LastParseTranslate); + 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)); - } + targetuser->WriteFrom(user, "MODE " + LastParse); + + FOREACH_MOD(OnMode, (user, targetuser, targetchannel, LastParseParams, LastParseTranslate)); } else if (targetchannel && parameters.size() == 2) { @@ -570,27 +535,36 @@ void ModeParser::DisplayListModes(User* user, Channel* chan, std::string &mode_s 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; 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); + user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You do not have access to view the +%c list", + 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 @@ -598,11 +572,6 @@ void ModeParser::DisplayListModes(User* user, Channel* chan, std::string &mode_s } } -const std::string& ModeParser::GetLastParse() -{ - return LastParse; -} - void ModeParser::CleanMask(std::string &mask) { std::string::size_type pos_of_pling = mask.find_first_of('!'); @@ -639,50 +608,83 @@ void ModeParser::CleanMask(std::string &mask) } } -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. @@ -691,106 +693,111 @@ bool ModeParser::DelMode(ModeHandler* mh) 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); + + irc::modestacker stack(false); + mh->RemoveMode(chan, stack); + + std::vector<std::string> stackresult; + stackresult.push_back(chan->name); + while (stack.GetStackedLine(stackresult)) + { + this->Process(stackresult, ServerInstance->FakeClient, MODE_LOCALONLY); + stackresult.erase(stackresult.begin() + 1, stackresult.end()); + } } + } 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 */ @@ -799,37 +806,38 @@ std::string ModeParser::GiveModeList(ModeMasks m) 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(); } } } @@ -843,15 +851,12 @@ std::string ModeParser::BuildPrefixes(bool lettersAndModes) 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++) @@ -863,91 +868,58 @@ std::string ModeParser::BuildPrefixes(bool lettersAndModes) 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); - } + 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, 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, irc::modestacker& stack) { - 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 + stack.Push(this->GetModeChar(), 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); - } + stack.Push(this->GetModeChar()); + } +} + +void PrefixMode::RemoveMode(Channel* chan, irc::modestacker& stack) +{ + const UserMembList* userlist = chan->GetUsers(); + for (UserMembCIter i = userlist->begin(); i != userlist->end(); ++i) + { + if (i->second->hasMode(this->GetModeChar())) + stack.Push(this->GetModeChar(), i->first->nick); } } @@ -967,45 +939,34 @@ struct builtin_modes ModeChannelOp o; ModeChannelVoice v; - ModeUserWallops uw; ModeUserInvisible 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); + 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(); + memset(modehandlersbyid, 0, sizeof(modehandlersbyid)); seq = 0; memset(&sent, 0, sizeof(sent)); - - static_modes.init(this); } ModeParser::~ModeParser() diff --git a/src/modes/cmode_b.cpp b/src/modes/cmode_b.cpp deleted file mode 100644 index 09df05100..000000000 --- a/src/modes/cmode_b.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/* - * 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; - } - chan->bans.erase(i); - return dest; - } - } - dest.clear(); - return dest; -} - diff --git a/src/modes/cmode_k.cpp b/src/modes/cmode_k.cpp index 400333fce..e14f93a77 100644 --- a/src/modes/cmode_k.cpp +++ b/src/modes/cmode_k.cpp @@ -24,76 +24,53 @@ #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; +} diff --git a/src/modes/cmode_l.cpp b/src/modes/cmode_l.cpp index 001d058bb..128854b50 100644 --- a/src/modes/cmode_l.cpp +++ b/src/modes/cmode_l.cpp @@ -23,9 +23,10 @@ #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') { } @@ -35,13 +36,13 @@ bool ModeChannelLimit::ResolveModeConflict(std::string &their_param, const std:: 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()); - - if (limit < 0) - return false; + ext.set(chan, ConvToInt(parameter)); + return MODEACTION_ALLOW; +} - parameter = ConvToStr(limit); - return true; +void ModeChannelLimit::SerializeParam(Channel* chan, intptr_t n, std::string& out) +{ + out += ConvToStr(n); } diff --git a/src/modes/cmode_o.cpp b/src/modes/cmode_o.cpp index 0a13b39ce..6e96afa67 100644 --- a/src/modes/cmode_o.cpp +++ b/src/modes/cmode_o.cpp @@ -26,45 +26,11 @@ #include "channels.h" #include "users.h" #include "modules.h" -#include "modes/cmode_o.h" +#include "builtinmodes.h" -ModeChannelOp::ModeChannelOp() : ModeHandler(NULL, "op", 'o', PARAM_ALWAYS, MODETYPE_CHANNEL) +ModeChannelOp::ModeChannelOp() : PrefixMode(NULL, "op", 'o') { - 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; + prefixrank = OP_VALUE; } diff --git a/src/modes/cmode_v.cpp b/src/modes/cmode_v.cpp index 4a00f60f1..c8ce30ab1 100644 --- a/src/modes/cmode_v.cpp +++ b/src/modes/cmode_v.cpp @@ -26,45 +26,11 @@ #include "channels.h" #include "users.h" #include "modules.h" -#include "modes/cmode_v.h" +#include "builtinmodes.h" -ModeChannelVoice::ModeChannelVoice() : ModeHandler(NULL, "voice", 'v', PARAM_ALWAYS, MODETYPE_CHANNEL) +ModeChannelVoice::ModeChannelVoice() : PrefixMode(NULL, "voice", 'v') { - 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; + prefixrank = VOICE_VALUE; } diff --git a/src/modes/umode_o.cpp b/src/modes/umode_o.cpp index a5f590ba0..affd6b50c 100644 --- a/src/modes/umode_o.cpp +++ b/src/modes/umode_o.cpp @@ -22,7 +22,7 @@ #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) { @@ -32,7 +32,7 @@ ModeUserOperator::ModeUserOperator() : ModeHandler(NULL, "oper", 'o', PARAM_NONE 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 */ @@ -46,8 +46,7 @@ ModeAction ModeUserOperator::OnModeChange(User* source, User* dest, Channel*, st * 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; diff --git a/src/modes/umode_s.cpp b/src/modes/umode_s.cpp index 1b782ae85..b355cb824 100644 --- a/src/modes/umode_s.cpp +++ b/src/modes/umode_s.cpp @@ -23,7 +23,7 @@ #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) { @@ -32,41 +32,116 @@ ModeUserServerNoticeMask::ModeUserServerNoticeMask() : ModeHandler(NULL, "snomas 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; +} diff --git a/src/modmanager_dynamic.cpp b/src/modmanager_dynamic.cpp index 7dae49a18..afb690207 100644 --- a/src/modmanager_dynamic.cpp +++ b/src/modmanager_dynamic.cpp @@ -22,7 +22,6 @@ #include "socket.h" #include "socketengine.h" #include "command_parse.h" -#include "dns.h" #include "exitcodes.h" #include <iostream> @@ -38,29 +37,32 @@ bool ModuleManager::Load(const std::string& filename, bool defer) 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) { @@ -71,28 +73,35 @@ bool ModuleManager::Load(const std::string& filename, bool defer) 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) { @@ -102,109 +111,41 @@ bool ModuleManager::Load(const std::string& filename, bool defer) 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 - 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); } @@ -213,57 +154,6 @@ void ModuleManager::LoadAll() 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 diff --git a/src/modmanager_static.cpp b/src/modmanager_static.cpp index 8f532ee80..ac127b703 100644 --- a/src/modmanager_static.cpp +++ b/src/modmanager_static.cpp @@ -17,7 +17,7 @@ */ -#define MODNAME cmd_all +#define MODNAME "cmd_all" #include "inspircd.h" #include "exitcodes.h" @@ -58,7 +58,6 @@ class AllModule : public Module { Command* c = (*i)(this); cmds.push_back(c); - ServerInstance->AddCommand(c); } } catch (...) @@ -70,8 +69,7 @@ class AllModule : public Module ~AllModule() { - for(std::vector<Command*>::iterator i = cmds.begin(); i != cmds.end(); ++i) - delete *i; + stdalgo::delete_all(cmds); } Version GetVersion() @@ -88,6 +86,11 @@ bool ModuleManager::Load(const std::string& name, bool defer) if (it == modlist->end()) return false; Module* mod = NULL; + + ServiceList newservices; + if (!defer) + this->NewServices = &newservices; + try { mod = (*it->second->init)(); @@ -95,146 +98,50 @@ bool ModuleManager::Load(const std::string& name, bool defer) 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 - 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 diff --git a/src/modules.cpp b/src/modules.cpp index b2d2f23c6..c27147820 100644 --- a/src/modules.cpp +++ b/src/modules.cpp @@ -24,26 +24,28 @@ */ +#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 intrusive_list<dynamic_reference_base>* dynrefs = NULL; +static bool dynref_init_complete = false; void dynamic_reference_base::reset_all() { + dynref_init_complete = true; if (!dynrefs) return; - for(unsigned int i = 0; i < dynrefs->size(); i++) - (*dynrefs)[i]->ClearCache(); + for (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 @@ -56,22 +58,11 @@ Version::Version(const std::string &desc, int flags, const std::string& linkdata { } -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) @@ -85,100 +76,110 @@ Module::~Module() { } -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 std::vector<std::string>&, const std::vector<TranslateType>&) { 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(User*, 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*, const std::vector<std::string>&) { 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); } +void Module::OnNamesListItem(User*, Membership*, std::string&, std::string&) { DetachEvent(I_OnNamesListItem); } +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() { } @@ -212,6 +213,12 @@ void ModuleManager::Attach(Implementation* i, Module* mod, size_t sz) 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) @@ -254,22 +261,25 @@ bool ModuleManager::SetPriority(Module* mod, Implementation i, Priority s, Modul 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) @@ -284,7 +294,7 @@ found_src: 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) { @@ -324,6 +334,29 @@ swap_now: 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); @@ -331,13 +364,13 @@ bool ModuleManager::CanUnload(Module* mod) 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; } @@ -347,12 +380,18 @@ bool ModuleManager::CanUnload(Module* mod) 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; @@ -362,7 +401,9 @@ void ModuleManager::DoSafeUnload(Module* mod) for(UserMembCIter 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 @@ -370,16 +411,25 @@ void ModuleManager::DoSafeUnload(Module* mod) 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++; @@ -389,19 +439,13 @@ void ModuleManager::DoSafeUnload(Module* mod) 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() @@ -427,19 +471,121 @@ 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 + 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) @@ -451,9 +597,13 @@ void ModuleManager::AddService(ServiceProvider& 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."); @@ -461,6 +611,9 @@ void ModuleManager::AddService(ServiceProvider& item) case SERVICE_DATA: case SERVICE_IOHOOK: { + if ((item.name.substr(0, 5) == "mode/") || (item.name.substr(0, 6) == "umode/")) + 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) @@ -468,6 +621,7 @@ void ModuleManager::AddService(ServiceProvider& item) 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: @@ -482,7 +636,7 @@ void ModuleManager::DelService(ServiceProvider& item) 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: { @@ -522,76 +676,35 @@ dynamic_reference_base::dynamic_reference_base(Module* Creator, const std::strin : name(Name), value(NULL), creator(Creator) { if (!dynrefs) - dynrefs = new std::vector<dynamic_reference_base*>; - dynrefs->push_back(this); + dynrefs = new intrusive_list<dynamic_reference_base>; + dynrefs->push_front(this); + if (dynref_init_complete) + 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) @@ -603,179 +716,3 @@ 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(); -} diff --git a/src/modules/extra/m_geoip.cpp b/src/modules/extra/m_geoip.cpp index a36c39bc8..394f7f9b4 100644 --- a/src/modules/extra/m_geoip.cpp +++ b/src/modules/extra/m_geoip.cpp @@ -27,7 +27,6 @@ # pragma comment(lib, "GeoIP.lib") #endif -/* $ModDesc: Provides a way to restrict users by country using GeoIP lookup */ /* $LinkerFlags: -lGeoIP */ class ModuleGeoIP : public Module @@ -37,7 +36,7 @@ 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"; @@ -51,16 +50,12 @@ class ModuleGeoIP : public Module { } - 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) { LocalUser* user = *i; @@ -77,12 +72,12 @@ class ModuleGeoIP : public Module 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) @@ -99,7 +94,7 @@ class ModuleGeoIP : public Module 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; @@ -115,7 +110,7 @@ class ModuleGeoIP : public Module 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)); @@ -129,4 +124,3 @@ class ModuleGeoIP : public Module }; MODULE_INIT(ModuleGeoIP) - diff --git a/src/modules/extra/m_ldap.cpp b/src/modules/extra/m_ldap.cpp new file mode 100644 index 000000000..63c29ed3a --- /dev/null +++ b/src/modules/extra/m_ldap.cpp @@ -0,0 +1,572 @@ +/* + * 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/ldap.h" + +#include <ldap.h> + +#ifdef _WIN32 +# pragma comment(lib, "ldap.lib") +# pragma comment(lib, "lber.lib") +#endif + +/* $LinkerFlags: -lldap */ + +class LDAPService : public LDAPProvider, public SocketThread +{ + LDAP* con; + reference<ConfigTag> config; + time_t last_connect; + int searchscope; + + 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] = i; + this->UnlockQueueWakeup(); + } + } + + public: + typedef std::map<int, 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) + { + 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; + + Connect(); + } + + ~LDAPService() + { + this->LockQueue(); + + for (query_queue::iterator i = this->queries.begin(); i != this->queries.end(); ++i) + ldap_abandon_ext(this->con, i->first, NULL, NULL); + this->queries.clear(); + + for (result_queue::iterator i = this->results.begin(); i != this->results.end(); ++i) + { + i->second->error = "LDAP Interface is going away"; + i->first->OnError(*i->second); + } + 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)); + } + } + + 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; + } + else + 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; + 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; + ++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) diff --git a/src/modules/extra/m_ldapauth.cpp b/src/modules/extra/m_ldapauth.cpp deleted file mode 100644 index 5b3f1e7cc..000000000 --- a/src/modules/extra/m_ldapauth.cpp +++ /dev/null @@ -1,436 +0,0 @@ -/* - * 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, "ldap.lib") -# pragma comment(lib, "lber.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) diff --git a/src/modules/extra/m_ldapoper.cpp b/src/modules/extra/m_ldapoper.cpp deleted file mode 100644 index 53896878c..000000000 --- a/src/modules/extra/m_ldapoper.cpp +++ /dev/null @@ -1,255 +0,0 @@ -/* - * 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, "ldap.lib") -# pragma comment(lib, "lber.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) diff --git a/src/modules/extra/m_mssql.cpp b/src/modules/extra/m_mssql.cpp index 598f9aac9..8f8fe080f 100644 --- a/src/modules/extra/m_mssql.cpp +++ b/src/modules/extra/m_mssql.cpp @@ -30,10 +30,8 @@ #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; @@ -64,8 +62,8 @@ class QueryThread : public SocketThread public: QueryThread(ModuleMsSQL* mod) : Parent(mod) { } ~QueryThread() { } - virtual void Run(); - virtual void OnNotify(); + void Run(); + void OnNotify(); }; class MsSQLResult : public SQLresult @@ -88,10 +86,6 @@ class MsSQLResult : public SQLresult { } - ~MsSQLResult() - { - } - void AddRow(int colsnum, char **dat, char **colname) { colnames.clear(); @@ -111,17 +105,17 @@ class MsSQLResult : public SQLresult 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()) { @@ -134,7 +128,7 @@ class MsSQLResult : public SQLresult return ""; } - virtual int ColNum(const std::string &column) + int ColNum(const std::string &column) { for (unsigned int i = 0; i < colnames.size(); i++) { @@ -145,7 +139,7 @@ class MsSQLResult : public SQLresult return 0; } - virtual SQLfield GetValue(int row, int column) + SQLfield GetValue(int row, int column) { if ((row >= 0) && (row < rows) && (column >= 0) && (column < Cols())) { @@ -158,7 +152,7 @@ class MsSQLResult : public SQLresult return SQLfield("",true); } - virtual SQLfieldList& GetRow() + SQLfieldList& GetRow() { if (currentrow < rows) return fieldlists[currentrow]; @@ -166,7 +160,7 @@ class MsSQLResult : public SQLresult 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... @@ -192,7 +186,7 @@ class MsSQLResult : public SQLresult return *fieldmap; } - virtual SQLfieldList* GetRowPtr() + SQLfieldList* GetRowPtr() { fieldlist = new SQLfieldList(); @@ -207,7 +201,7 @@ class MsSQLResult : public SQLresult return fieldlist; } - virtual SQLfieldMap* GetRowMapPtr() + SQLfieldMap* GetRowMapPtr() { fieldmap = new SQLfieldMap(); @@ -223,12 +217,12 @@ class MsSQLResult : public SQLresult return fieldmap; } - virtual void Free(SQLfieldMap* fm) + void Free(SQLfieldMap* fm) { delete fm; } - virtual void Free(SQLfieldList* fl) + void Free(SQLfieldList* fl) { delete fl; } @@ -258,7 +252,7 @@ class SQLConn : public classbase 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(); } @@ -266,7 +260,7 @@ class SQLConn : public classbase 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(); } @@ -274,7 +268,7 @@ class SQLConn : public classbase 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(); } @@ -433,7 +427,7 @@ class SQLConn : public classbase 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) { @@ -449,8 +443,8 @@ class SQLConn : public classbase 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: @@ -476,8 +470,8 @@ class SQLConn : public classbase 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]; @@ -516,7 +510,7 @@ class SQLConn : public classbase { 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; } @@ -525,7 +519,7 @@ class SQLConn : public classbase { 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; } @@ -657,18 +651,14 @@ class ModuleMsSQL : public Module 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); } - virtual ~ModuleMsSQL() + ~ModuleMsSQL() { queryDispatcher->join(); delete queryDispatcher; @@ -753,7 +743,7 @@ class ModuleMsSQL : public Module 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; } @@ -787,14 +777,14 @@ class ModuleMsSQL : public Module 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) { @@ -825,7 +815,7 @@ class ModuleMsSQL : public Module return ++currid; } - virtual Version GetVersion() + Version GetVersion() CXX11_OVERRIDE { return Version("MsSQL provider", VF_VENDOR); } diff --git a/src/modules/extra/m_mysql.cpp b/src/modules/extra/m_mysql.cpp index 22cf5f3f4..3aed09416 100644 --- a/src/modules/extra/m_mysql.cpp +++ b/src/modules/extra/m_mysql.cpp @@ -25,7 +25,7 @@ #include "inspircd.h" #include <mysql.h> -#include "sql.h" +#include "modules/sql.h" #ifdef _WIN32 # pragma comment(lib, "mysqlclient.lib") @@ -35,7 +35,6 @@ /* 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") */ @@ -107,11 +106,11 @@ class ModuleSQL : public Module 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 @@ -121,8 +120,8 @@ 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 @@ -188,21 +187,17 @@ class MySQLresult : public SQLResult } - ~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())) { @@ -211,7 +206,7 @@ class MySQLresult : public SQLResult return SQLEntry(); } - virtual bool GetRow(SQLEntries& result) + bool GetRow(SQLEntries& result) { if (currentrow < rows) { @@ -386,11 +381,6 @@ 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); } ModuleSQL::~ModuleSQL() @@ -407,7 +397,7 @@ ModuleSQL::~ModuleSQL() } } -void ModuleSQL::OnRehash(User* user) +void ModuleSQL::ReadConfig(ConfigStatus& status) { ConnMap conns; ConfigTagList tags = ServerInstance->Config->ConfTags("database"); diff --git a/src/modules/extra/m_pgsql.cpp b/src/modules/extra/m_pgsql.cpp index ac247548a..e6abbfcf9 100644 --- a/src/modules/extra/m_pgsql.cpp +++ b/src/modules/extra/m_pgsql.cpp @@ -26,9 +26,8 @@ #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 */ @@ -62,7 +61,7 @@ class ReconnectTimer : public Timer ReconnectTimer(ModulePgSQL* m) : Timer(5, ServerInstance->Time(), false), mod(m) { } - virtual void Tick(time_t TIME); + bool Tick(time_t TIME); }; struct QueueItem @@ -97,12 +96,12 @@ class PgSQLresult : public SQLResult 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++) @@ -111,7 +110,7 @@ class PgSQLresult : public SQLResult } } - 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)) @@ -120,7 +119,7 @@ class PgSQLresult : public SQLResult return SQLEntry(std::string(v, PQgetlength(res, row, column))); } - virtual bool GetRow(SQLEntries& result) + bool GetRow(SQLEntries& result) { if (currentrow >= PQntuples(res)) return false; @@ -152,7 +151,7 @@ class SQLConn : public SQLProvider, public EventHandler { 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(); } } @@ -180,7 +179,7 @@ class SQLConn : public SQLProvider, public EventHandler } } - virtual void HandleEvent(EventType et, int errornum) + void HandleEvent(EventType et, int errornum) { switch (et) { @@ -242,9 +241,9 @@ class SQLConn : public SQLProvider, public EventHandler 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; } @@ -257,17 +256,17 @@ class SQLConn : public SQLProvider, public EventHandler 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: @@ -350,17 +349,17 @@ restart: 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: @@ -417,7 +416,7 @@ restart: 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 @@ -452,7 +451,7 @@ restart: 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 @@ -488,7 +487,7 @@ restart: void Close() { - ServerInstance->SE->DelFd(this); + SocketEngine::DelFd(this); if(sql) { @@ -505,25 +504,17 @@ class ModulePgSQL : public Module 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(); } @@ -564,7 +555,7 @@ class ModulePgSQL : public Module 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++) @@ -592,16 +583,18 @@ class ModulePgSQL : public Module } } - 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() @@ -615,7 +608,7 @@ void SQLConn::DelayReconnect() if (!mod->retimer) { mod->retimer = new ReconnectTimer(mod); - ServerInstance->Timers->AddTimer(mod->retimer); + ServerInstance->Timers.AddTimer(mod->retimer); } } } diff --git a/src/modules/extra/m_regex_pcre.cpp b/src/modules/extra/m_regex_pcre.cpp index cba234c8c..9ae6719ba 100644 --- a/src/modules/extra/m_regex_pcre.cpp +++ b/src/modules/extra/m_regex_pcre.cpp @@ -20,10 +20,8 @@ #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 */ @@ -31,21 +29,11 @@ # 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; @@ -53,24 +41,19 @@ public: 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); } }; @@ -78,7 +61,7 @@ class PCREFactory : public RegexFactory { 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); } @@ -86,13 +69,13 @@ class PCREFactory : public RegexFactory 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); } diff --git a/src/modules/extra/m_regex_posix.cpp b/src/modules/extra/m_regex_posix.cpp index b3afd60c8..b5fddfab8 100644 --- a/src/modules/extra/m_regex_posix.cpp +++ b/src/modules/extra/m_regex_posix.cpp @@ -19,28 +19,15 @@ #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; @@ -58,23 +45,18 @@ public: 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); } }; @@ -83,7 +65,7 @@ class PosixFactory : public RegexFactory 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); } @@ -92,20 +74,18 @@ class PosixFactory : public RegexFactory 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"); } diff --git a/src/modules/extra/m_regex_re2.cpp b/src/modules/extra/m_regex_re2.cpp new file mode 100644 index 000000000..544e3060e --- /dev/null +++ b/src/modules/extra/m_regex_re2.cpp @@ -0,0 +1,77 @@ +/* + * 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/>. + */ + + +#if defined __GNUC__ +# pragma GCC diagnostic ignored "-Wshadow" +#endif + +#include "inspircd.h" +#include "modules/regex.h" +#include <re2/re2.h> + + +/* $CompileFlags: -std=c++11 */ +/* $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) diff --git a/src/modules/extra/m_regex_stdlib.cpp b/src/modules/extra/m_regex_stdlib.cpp index 204728b65..8e7bd0da2 100644 --- a/src/modules/extra/m_regex_stdlib.cpp +++ b/src/modules/extra/m_regex_stdlib.cpp @@ -15,32 +15,18 @@ * 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{ @@ -48,11 +34,11 @@ public: } 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); } @@ -63,7 +49,7 @@ class StdRegexFactory : public RegexFactory 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); } @@ -73,23 +59,20 @@ class ModuleRegexStd : public Module { 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") diff --git a/src/modules/extra/m_regex_tre.cpp b/src/modules/extra/m_regex_tre.cpp index 4b9eab472..8a1d54248 100644 --- a/src/modules/extra/m_regex_tre.cpp +++ b/src/modules/extra/m_regex_tre.cpp @@ -19,27 +19,15 @@ #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: @@ -60,30 +48,26 @@ 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); } @@ -92,18 +76,15 @@ class TREFactory : public RegexFactory { 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); } }; diff --git a/src/modules/extra/m_sqlite3.cpp b/src/modules/extra/m_sqlite3.cpp index 7f6a53359..1c213e8e0 100644 --- a/src/modules/extra/m_sqlite3.cpp +++ b/src/modules/extra/m_sqlite3.cpp @@ -22,16 +22,14 @@ #include "inspircd.h" #include <sqlite3.h> -#include "sql.h" +#include "modules/sql.h" #ifdef _WIN32 # pragma comment(lib, "sqlite3.lib") #endif -/* $ModDesc: sqlite3 provider */ -/* $CompileFlags: pkgconfversion("sqlite3","3.3") pkgconfincludes("sqlite3","/sqlite3.h","") */ +/* $CompileFlags: pkgconfversion("sqlite3","3.3") pkgconfincludes("sqlite3","/sqlite3.h","") -Wno-pedantic */ /* $LinkerFlags: pkgconflibs("sqlite3","/libsqlite3.so","-lsqlite3") */ -/* $NoPedantic */ class SQLConn; typedef std::map<std::string, SQLConn*> ConnMap; @@ -48,16 +46,12 @@ class SQLite3Result : public SQLResult { } - ~SQLite3Result() - { - } - - virtual int Rows() + int Rows() { return rows; } - virtual bool GetRow(SQLEntries& result) + bool GetRow(SQLEntries& result) { if (currentrow < rows) { @@ -72,7 +66,7 @@ class SQLite3Result : public SQLResult } } - virtual void GetCols(std::vector<std::string>& result) + void GetCols(std::vector<std::string>& result) { result.assign(columns.begin(), columns.end()); } @@ -80,7 +74,6 @@ class SQLite3Result : public SQLResult class SQLConn : public SQLProvider { - private: sqlite3* conn; reference<ConfigTag> config; @@ -90,7 +83,7 @@ class SQLConn : public SQLProvider 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; } } @@ -149,13 +142,13 @@ class SQLConn : public SQLProvider 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; @@ -176,7 +169,7 @@ class SQLConn : public SQLProvider 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++) @@ -206,23 +199,10 @@ class SQLConn : public SQLProvider 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(); } @@ -238,7 +218,7 @@ class ModuleSQLite3 : public Module conns.clear(); } - void ReadConf() + void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE { ClearConns(); ConfigTagList tags = ServerInstance->Config->ConfTags("database"); @@ -252,12 +232,7 @@ class ModuleSQLite3 : public Module } } - void OnRehash(User* user) - { - ReadConf(); - } - - Version GetVersion() + Version GetVersion() CXX11_OVERRIDE { return Version("sqlite3 provider", VF_VENDOR); } diff --git a/src/modules/extra/m_ssl_gnutls.cpp b/src/modules/extra/m_ssl_gnutls.cpp index 1f1297ef9..5702ed2d5 100644 --- a/src/modules/extra/m_ssl_gnutls.cpp +++ b/src/modules/extra/m_ssl_gnutls.cpp @@ -22,11 +22,22 @@ #include "inspircd.h" -#include <gcrypt.h> #include <gnutls/gnutls.h> #include <gnutls/x509.h> -#include "ssl.h" -#include "m_cap.h" +#include "modules/ssl.h" +#include "modules/cap.h" +#include <memory> + +#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.lib") @@ -39,10 +50,8 @@ # pragma comment(lib, "gdi32.lib") #endif -/* $ModDesc: Provides SSL support for clients */ -/* $CompileFlags: pkgconfincludes("gnutls","/gnutls/gnutls.h","") exec("libgcrypt-config --cflags") */ -/* $LinkerFlags: rpath("pkg-config --libs gnutls") pkgconflibs("gnutls","/libgnutls.so","-lgnutls") 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'") -Wno-pedantic */ +/* $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 @@ -62,586 +71,769 @@ typedef gnutls_dh_params_t gnutls_dh_params; enum issl_status { ISSL_NONE, ISSL_HANDSHAKING_READ, ISSL_HANDSHAKING_WRITE, ISSL_HANDSHAKEN, ISSL_CLOSING, ISSL_CLOSED }; -static std::vector<gnutls_x509_crt_t> x509_certs; -static gnutls_x509_privkey_t x509_key; -#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 - st->ncerts = x509_certs.size(); - st->cert.x509 = &x509_certs[0]; - st->key.x509 = 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 + gnutls_rnd(GNUTLS_RND_RANDOM, buffer, len); +#else gcry_randomize(buffer, len, GCRY_STRONG_RANDOM); +#endif } }; -/** Represents an SSL user's extra data - */ -class issl_session -{ -public: - StreamSocket* socket; - gnutls_session_t sess; - issl_status status; - reference<ssl_cert> cert; - - issl_session() : socket(NULL), sess(NULL) {} -}; - -class CommandStartTLS : public SplitCommand +namespace GnuTLS { - public: - bool enabled; - CommandStartTLS (Module* mod) : SplitCommand(mod, "STARTTLS") + class Init { - enabled = true; - works_before_reg = true; - } + public: + Init() { gnutls_global_init(); } + ~Init() { gnutls_global_deinit(); } + }; - CmdResult HandleLocal(const std::vector<std::string> ¶meters, LocalUser *user) + class Exception : public ModuleException { - if (!enabled) - { - user->WriteNumeric(691, "%s :STARTTLS is not enabled", user->nick.c_str()); - return CMD_FAILURE; - } + public: + Exception(const std::string& reason) + : ModuleException(reason) { } + }; - if (user->registered == REG_ALL) - { - user->WriteNumeric(691, "%s :STARTTLS is not permitted after client registration is complete", user->nick.c_str()); - } - else + void ThrowOnError(int errcode, const char* msg) + { + if (errcode < 0) { - if (!user->eh.GetIOHook()) - { - 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); - } - else - user->WriteNumeric(691, "%s :STARTTLS failure", user->nick.c_str()); + std::string reason = msg; + reason.append(" :").append(gnutls_strerror(errcode)); + throw Exception(reason); } - - return CMD_FAILURE; } -}; - -class ModuleSSLGnuTLS : public Module -{ - issl_session* sessions; - - gnutls_certificate_credentials_t x509_cred; - gnutls_dh_params_t dh_params; - gnutls_digest_algorithm_t hash; - #ifdef GNUTLS_NEW_PRIO_API - gnutls_priority_t priority; - #endif - - std::string sslports; - int dh_bits; - bool cred_alloc; - bool dh_alloc; + /** Used to create a gnutls_datum_t* from a std::string + */ + class Datum + { + gnutls_datum_t datum; - RandGen randhandler; - CommandStartTLS starttls; + public: + Datum(const std::string& dat) + { + datum.data = (unsigned char*)dat.data(); + datum.size = static_cast<unsigned int>(dat.length()); + } - GenericCap capHandler; - ServiceProvider iohook; + const gnutls_datum_t* get() const { return &datum; } + }; - inline static const char* UnknownIfNULL(const char* str) + class Hash { - return str ? str : "UNKNOWN"; - } + gnutls_digest_algorithm_t hash; - static ssize_t gnutls_pull_wrapper(gnutls_transport_ptr_t session_wrap, void* buffer, size_t size) - { - issl_session* session = reinterpret_cast<issl_session*>(session_wrap); - if (session->socket->GetEventMask() & FD_READ_WILL_BLOCK) + public: + // Nothing to deallocate, constructor may throw freely + Hash(const std::string& hashname) { -#ifdef _WIN32 - gnutls_transport_set_errno(session->sess, EAGAIN); + // 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 - errno = EAGAIN; + if (hashname == "md5") + hash = GNUTLS_DIG_MD5; + else if (hashname == "sha1") + hash = GNUTLS_DIG_SHA1; + else + throw Exception("Unknown hash type " + hashname); #endif - return -1; } - int rv = ServerInstance->SE->Recv(session->socket, reinterpret_cast<char *>(buffer), size, 0); + gnutls_digest_algorithm_t get() const { return hash; } + }; -#ifdef _WIN32 - if (rv < 0) + class DHParams + { + gnutls_dh_params_t dh_params; + + DHParams() { - /* 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); + ThrowOnError(gnutls_dh_params_init(&dh_params), "gnutls_dh_params_init() failed"); } -#endif - if (rv < (int)size) - ServerInstance->SE->ChangeEventMask(session->socket, FD_READ_WILL_BLOCK); - return rv; - } - - static ssize_t gnutls_push_wrapper(gnutls_transport_ptr_t session_wrap, const void* buffer, size_t size) - { - issl_session* session = reinterpret_cast<issl_session*>(session_wrap); - if (session->socket->GetEventMask() & FD_WRITE_WILL_BLOCK) + public: + /** Import */ + static std::auto_ptr<DHParams> Import(const std::string& dhstr) { -#ifdef _WIN32 - gnutls_transport_set_errno(session->sess, EAGAIN); -#else - errno = EAGAIN; -#endif - return -1; + 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; } - int rv = ServerInstance->SE->Send(session->socket, reinterpret_cast<const char *>(buffer), size, 0); - -#ifdef _WIN32 - if (rv < 0) + /** Generate */ + static std::auto_ptr<DHParams> Generate(unsigned int bits) { - /* 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); + std::auto_ptr<DHParams> dh(new DHParams); + ThrowOnError(gnutls_dh_params_generate2(dh->dh_params, bits), "Unable to generate DH params"); + return dh; } -#endif - if (rv < (int)size) - ServerInstance->SE->ChangeEventMask(session->socket, FD_WRITE_WILL_BLOCK); - return rv; - } + ~DHParams() + { + gnutls_dh_params_deinit(dh_params); + } - public: + const gnutls_dh_params_t& get() const { return dh_params; } + }; - ModuleSSLGnuTLS() - : starttls(this), capHandler(this, "tls"), iohook(this, "ssl/gnutls", SERVICE_IOHOOK) + class X509Key { - gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); + /** Ensure that the key is deinited in case the constructor of X509Key throws + */ + class RAIIKey + { + public: + gnutls_x509_privkey_t key; - sessions = new issl_session[ServerInstance->SE->GetMaxFds()]; + RAIIKey() + { + ThrowOnError(gnutls_x509_privkey_init(&key), "gnutls_x509_privkey_init() failed"); + } - gnutls_global_init(); // This must be called once in the program - gnutls_x509_privkey_init(&x509_key); + ~RAIIKey() + { + gnutls_x509_privkey_deinit(key); + } + } key; - #ifdef GNUTLS_NEW_PRIO_API - // Init this here so it's always initialized, avoids an extra boolean - gnutls_priority_init(&priority, "NORMAL", NULL); - #endif + 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"); + } - cred_alloc = false; - dh_alloc = false; - } + gnutls_x509_privkey_t& get() { return key.key; } + }; - void init() + class X509CertList { - // Needs the flag as it ignores a plain /rehash - OnModuleRehash(NULL,"ssl"); + std::vector<gnutls_x509_crt_t> certs; - ServerInstance->GenRandom = &randhandler; + public: + /** Import */ + X509CertList(const std::string& certstr) + { + unsigned int certcount = 3; + certs.resize(certcount); + Datum datum(certstr); + + 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); + } - // Void return, guess we assume success - gnutls_certificate_set_dh_params(x509_cred, dh_params); - Implementation eventlist[] = { I_On005Numeric, I_OnRehash, I_OnModuleRehash, I_OnUserConnect, - I_OnEvent, I_OnHookIO }; - ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation)); + ThrowOnError(ret, "Unable to load certificates"); - ServerInstance->Modules->AddService(iohook); - ServerInstance->Modules->AddService(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); + } - void OnRehash(User* user) - { - sslports.clear(); + ~X509CertList() + { + for (std::vector<gnutls_x509_crt_t>::iterator i = certs.begin(); i != certs.end(); ++i) + gnutls_x509_crt_deinit(*i); + } - ConfigTag* Conf = ServerInstance->Config->ConfValue("gnutls"); - starttls.enabled = Conf->getBool("starttls", true); + gnutls_x509_crt_t* raw() { return &certs[0]; } + unsigned int size() const { return certs.size(); } + }; - if (Conf->getBool("showports", true)) + class X509CRL : public refcountbase + { + class RAIICRL { - sslports = Conf->getString("advertisedports"); - if (!sslports.empty()) - return; + public: + gnutls_x509_crl_t crl; - for (size_t i = 0; i < ServerInstance->ports.size(); i++) + RAIICRL() { - ListenSocket* port = ServerInstance->ports[i]; - if (port->bind_tag->getString("ssl") != "gnutls") - continue; + ThrowOnError(gnutls_x509_crl_init(&crl), "gnutls_x509_crl_init() failed"); + } - 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()); + ~RAIICRL() + { + gnutls_x509_crl_deinit(crl); + } + } crl; - if (port->bind_tag->getString("type", "clients") == "clients" && port->bind_addr != "127.0.0.1") - { - /* - * 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; - } + public: + /** Import */ + X509CRL(const std::string& crlstr) + { + int ret = gnutls_x509_crl_import(get(), Datum(crlstr).get(), GNUTLS_X509_FMT_PEM); + ThrowOnError(ret, "Unable to load certificate revocation list"); + } + + gnutls_x509_crl_t& get() { return crl.crl; } + }; + +#ifdef GNUTLS_NEW_PRIO_API + class Priority + { + gnutls_priority_t priority; + + public: + Priority(const std::string& priorities) + { + // 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))); } } - } - void OnModuleRehash(User* user, const std::string ¶m) + ~Priority() + { + gnutls_priority_deinit(priority); + } + + void SetupSession(gnutls_session_t sess) + { + gnutls_priority_set(sess, priority); + } + }; +#else + /** Dummy class, used when gnutls_priority_set() is not available + */ + class Priority { - if(param != "ssl") - return; + 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"); + } - std::string keyfile; - std::string certfile; - std::string cafile; - std::string crlfile; - OnRehash(user); - - ConfigTag* Conf = ServerInstance->Config->ConfValue("gnutls"); - - 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"); - - // 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"); - - if((dh_bits != 768) && (dh_bits != 1024) && (dh_bits != 2048) && (dh_bits != 3072) && (dh_bits != 4096)) - dh_bits = 1024; - - if (hashname == "md5") - hash = GNUTLS_DIG_MD5; - else if (hashname == "sha1") - hash = GNUTLS_DIG_SHA1; - else - throw ModuleException("Unknown hash type " + hashname); + static void SetupSession(gnutls_session_t sess) + { + // Always set the default priorities + gnutls_set_default_priority(sess); + } + }; +#endif + class CertCredentials + { + /** DH parameters associated with these credentials + */ + std::auto_ptr<DHParams> dh; - int ret; + protected: + gnutls_certificate_credentials_t cred; - if (dh_alloc) + public: + CertCredentials() { - gnutls_dh_params_deinit(dh_params); - dh_alloc = false; - dh_params = NULL; + ThrowOnError(gnutls_certificate_allocate_credentials(&cred), "Cannot allocate certificate credentials"); } - if (cred_alloc) + ~CertCredentials() { - // Deallocate the old credentials - gnutls_certificate_free_credentials(x509_cred); + gnutls_certificate_free_credentials(cred); + } - for(unsigned int i=0; i < x509_certs.size(); i++) - gnutls_x509_crt_deinit(x509_certs[i]); - x509_certs.clear(); + /** Associates these credentials with the session + */ + void SetupSession(gnutls_session_t sess) + { + gnutls_credentials_set(sess, GNUTLS_CRD_CERTIFICATE, cred); } - ret = gnutls_certificate_allocate_credentials(&x509_cred); - cred_alloc = (ret >= 0); - if (!cred_alloc) - ServerInstance->Logs->Log("m_ssl_gnutls",DEBUG, "m_ssl_gnutls.so: Failed to allocate certificate credentials: %s", gnutls_strerror(ret)); + /** 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()); + } + }; - 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)); + class X509Credentials : public CertCredentials + { + /** Private key + */ + X509Key key; - 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)); + /** Certificate list, presented to the peer + */ + X509CertList certs; - FileReader reader; + /** Trusted CA, may be NULL + */ + std::auto_ptr<X509CertList> trustedca; - 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()) }; + /** Certificate revocation list, may be NULL + */ + std::auto_ptr<X509CRL> crl; - 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()) }; + 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 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) + public: + X509Credentials(const std::string& certstr, const std::string& keystr) + : key(keystr) + , certs(certstr) { - // 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); + // 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"); + +#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 } - if (ret <= 0) + /** 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) { - // 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")); + // 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 (CRL.get()) + { + ret = gnutls_certificate_set_x509_crl(cred, &CRL->get(), 1); + ThrowOnError(ret, "gnutls_certificate_set_x509_crl() failed"); + } + + trustedca = certlist; + crl = CRL; + } } - x509_certs.resize(ret); + }; - 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))); + class Profile : public refcountbase + { + /** Name of this profile + */ + const std::string name; - 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))); + /** X509 certificate(s) and key + */ + X509Credentials x509cred; - #ifdef GNUTLS_NEW_PRIO_API - // It's safe to call this every time as we cannot have this uninitialized, see constructor and below. - gnutls_priority_deinit(priority); + /** The minimum length in bits for the DH prime to be accepted as a client + */ + unsigned int min_dh_bits; - // 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; + /** Hashing algorithm to use when generating certificate fingerprints + */ + Hash hash; - if ((ret = gnutls_priority_init(&priority, priocstr, &prioerror)) < 0) + /** 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) { - // 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); + x509cred.SetDH(DH); + x509cred.SetCA(CA, CRL); } - #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 - ret = gnutls_dh_params_init(&dh_params); - dh_alloc = (ret >= 0); - if (!dh_alloc) + static std::string ReadFile(const std::string& filename) { - ServerInstance->Logs->Log("m_ssl_gnutls",DEFAULT, "m_ssl_gnutls.so: Failed to initialise DH parameters: %s", gnutls_strerror(ret)); - return; + FileReader reader(filename); + std::string ret = reader.GetString(); + if (ret.empty()) + throw Exception("Cannot read file " + filename); + return ret; } - std::string dhfile = Conf->getString("dhfile"); - if (!dhfile.empty()) + public: + static reference<Profile> Create(const std::string& profilename, ConfigTag* tag) { - // 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 certstr = ReadFile(tag->getString("certfile", "cert.pem")); + std::string keystr = ReadFile(tag->getString("keyfile", "key.pem")); - if ((ret = gnutls_dh_params_import_pkcs3(dh_params, &dh_datum, GNUTLS_X509_FMT_PEM)) < 0) + 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()) { - // 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(); + ca.reset(new X509CertList(ReadFile(filename))); + + filename = tag->getString("crlfile"); + if (!filename.empty()) + crl.reset(new X509CRL(ReadFile(filename))); } + + return new Profile(profilename, certstr, keystr, dh, mindh, hashstr, priostr, ca, crl); } - else + + /** Set up the given session with the settings in this profile + */ + void SetupSession(gnutls_session_t sess) { - GenerateDHParams(); + priority.SetupSession(sess); + x509cred.SetupSession(sess); + gnutls_dh_set_prime_bits(sess, min_dh_bits); } - } - - void GenerateDHParams() - { - // 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. - if (!dh_alloc) - return; - - int ret; + const std::string& GetName() const { return name; } + X509Credentials& GetX509Credentials() { return x509cred; } + gnutls_digest_algorithm_t GetHash() const { return hash.get(); } + }; +} - 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)); - } +class GnuTLSIOHook : public SSLIOHook +{ + private: + gnutls_session_t sess; + issl_status status; + reference<GnuTLS::Profile> profile; - ~ModuleSSLGnuTLS() + void InitSession(StreamSocket* user, bool me_server) { - for(unsigned int i=0; i < x509_certs.size(); i++) - gnutls_x509_crt_deinit(x509_certs[i]); + gnutls_init(&sess, me_server ? GNUTLS_SERVER : GNUTLS_CLIENT); - gnutls_x509_privkey_deinit(x509_key); - #ifdef GNUTLS_NEW_PRIO_API - gnutls_priority_deinit(priority); - #endif + 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 (dh_alloc) - gnutls_dh_params_deinit(dh_params); - if (cred_alloc) - gnutls_certificate_free_credentials(x509_cred); + if (me_server) + gnutls_certificate_server_set_request(sess, GNUTLS_CERT_REQUEST); // Request client certificate if any. + } - gnutls_global_deinit(); - delete[] sessions; - ServerInstance->GenRandom = &ServerInstance->HandleGenRandom; + void CloseSession() + { + if (this->sess) + { + gnutls_bye(this->sess, GNUTLS_SHUT_WR); + gnutls_deinit(this->sess); + } + sess = NULL; + certificate = NULL; + status = ISSL_NONE; } - void OnCleanup(int target_type, void* item) + bool Handshake(StreamSocket* user) { - if(target_type == TYPE_USER) + int ret = gnutls_handshake(this->sess); + + if (ret < 0) { - LocalUser* user = IS_LOCAL(static_cast<User*>(item)); + if(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) + { + // Handshake needs resuming later, read() or write() would have blocked. - if (user && user->eh.GetIOHook() == this) + 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 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"); + 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; - Version GetVersion() - { - return Version("Provides SSL support for clients", VF_VENDOR); - } + VerifyCertificate(); + // Finish writing, if any left + SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE); - void On005Numeric(std::string &output) - { - if (!sslports.empty()) - output.append(" SSL=" + sslports); - if (starttls.enabled) - output.append(" STARTTLS"); + return true; + } } - void OnHookIO(StreamSocket* user, ListenSocket* lsb) + void VerifyCertificate() { - if (!user->GetIOHook() && lsb->bind_tag->getString("ssl") == "gnutls") + 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; + + /* 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 (ret < 0) { - /* Hook the user with our module */ - user->AddIOHook(this); + certinfo->error = std::string(gnutls_strerror(ret)); + return; } - } - void OnRequest(Request& request) - { - if (strcmp("GET_SSL_CERT", request.id) == 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) + { + certinfo->error = "No X509 keys sent"; + return; + } + + ret = gnutls_x509_crt_init(&cert); + if (ret < 0) + { + certinfo->error = gnutls_strerror(ret); + return; + } + + cert_list_size = 0; + cert_list = gnutls_certificate_get_peers(this->sess, &cert_list_size); + if (cert_list == NULL) + { + certinfo->error = "No certificate was found"; + goto info_done_dealloc; + } + + /* This is not a real world example, since we only check the first + * certificate in the given chain. + */ + + 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; + } + + gnutls_x509_crt_get_dn(cert, str, &name_size); + certinfo->dn = str; + + gnutls_x509_crt_get_issuer_dn(cert, str, &name_size); + certinfo->issuer = str; + + if ((ret = gnutls_x509_crt_get_fingerprint(cert, profile->GetHash(), digest, &digest_size)) < 0) + { + certinfo->error = gnutls_strerror(ret); + } + else { - SocketCertificateRequest& req = static_cast<SocketCertificateRequest&>(request); - int fd = req.sock->GetFd(); - issl_session* session = &sessions[fd]; + certinfo->fingerprint = BinToHex(digest, digest_size); + } - req.cert = session->cert; + /* 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())) + { + certinfo->error = "Not activated, or expired certificate"; } + +info_done_dealloc: + gnutls_x509_crt_deinit(cert); } - void InitSession(StreamSocket* user, bool me_server) + static const char* UnknownIfNULL(const char* str) { - issl_session* session = &sessions[user->GetFd()]; + return str ? str : "UNKNOWN"; + } - gnutls_init(&session->sess, me_server ? GNUTLS_SERVER : GNUTLS_CLIENT); - session->socket = user; + static ssize_t gnutls_pull_wrapper(gnutls_transport_ptr_t session_wrap, void* buffer, size_t size) + { + StreamSocket* sock = reinterpret_cast<StreamSocket*>(session_wrap); +#ifdef _WIN32 + GnuTLSIOHook* session = static_cast<GnuTLSIOHook*>(sock->GetIOHook()); +#endif - #ifdef GNUTLS_NEW_PRIO_API - gnutls_priority_set(session->sess, priority); - #else - gnutls_set_default_priority(session->sess); - #endif - gnutls_credentials_set(session->sess, GNUTLS_CRD_CERTIFICATE, 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 (sock->GetEventMask() & FD_READ_WILL_BLOCK) + { +#ifdef _WIN32 + gnutls_transport_set_errno(session->sess, EAGAIN); +#else + errno = EAGAIN; +#endif + return -1; + } - if (me_server) - gnutls_certificate_server_set_request(session->sess, GNUTLS_CERT_REQUEST); // Request client certificate if any. + int rv = SocketEngine::Recv(sock, reinterpret_cast<char *>(buffer), size, 0); - Handshake(session, user); +#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 + + if (rv < (int)size) + SocketEngine::ChangeEventMask(sock, FD_READ_WILL_BLOCK); + return rv; } - void OnStreamSocketAccept(StreamSocket* user, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) + static ssize_t gnutls_push_wrapper(gnutls_transport_ptr_t session_wrap, const void* buffer, size_t size) { - issl_session* session = &sessions[user->GetFd()]; + StreamSocket* sock = reinterpret_cast<StreamSocket*>(session_wrap); +#ifdef _WIN32 + GnuTLSIOHook* session = static_cast<GnuTLSIOHook*>(sock->GetIOHook()); +#endif - /* For STARTTLS: Don't try and init a session on a socket that already has a session */ - if (session->sess) - return; + if (sock->GetEventMask() & FD_WRITE_WILL_BLOCK) + { +#ifdef _WIN32 + gnutls_transport_set_errno(session->sess, EAGAIN); +#else + errno = EAGAIN; +#endif + return -1; + } + + int rv = SocketEngine::Send(sock, reinterpret_cast<const char *>(buffer), size, 0); + +#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); @@ -654,68 +846,66 @@ class ModuleSSLGnuTLS : public Module 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; } } @@ -723,185 +913,198 @@ class ModuleSSLGnuTLS : public Module 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 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; +} + +class GnuTLSIOHookProvider : public refcountbase, public IOHookProvider +{ + reference<GnuTLS::Profile> profile; - void OnUserConnect(LocalUser* user) + 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; + 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()); + } + + newprofiles.push_back(new GnuTLSIOHookProvider(this, profile)); } - gnutls_x509_crt_get_dn(cert, name, &name_size); - certinfo->dn = name; + // 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; + } - gnutls_x509_crt_get_issuer_dn(cert, name, &name_size); - certinfo->issuer = name; + void OnModuleRehash(User* user, const std::string ¶m) CXX11_OVERRIDE + { + if(param != "ssl") + return; - if ((ret = gnutls_x509_crt_get_fingerprint(cert, hash, digest, &digest_size)) < 0) + 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); } }; diff --git a/src/modules/extra/m_ssl_openssl.cpp b/src/modules/extra/m_ssl_openssl.cpp index 9a54ff80b..7a8bdeeb0 100644 --- a/src/modules/extra/m_ssl_openssl.cpp +++ b/src/modules/extra/m_ssl_openssl.cpp @@ -29,11 +29,12 @@ # define __AVAILABILITYMACROS__ # define DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER #endif - + #include "inspircd.h" +#include "iohook.h" #include <openssl/ssl.h> #include <openssl/err.h> -#include "ssl.h" +#include "modules/ssl.h" #ifdef _WIN32 # pragma comment(lib, "libcrypto.lib") @@ -47,14 +48,8 @@ # 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 */ - +/* $CompileFlags: pkgconfversion("openssl","0.9.7") pkgconfincludes("openssl","/openssl/ssl.h","") -Wno-pedantic */ +/* $LinkerFlags: rpath("pkg-config --libs openssl") pkgconflibs("openssl","/libssl.so","-lssl -lcrypto") */ enum issl_status { ISSL_NONE, ISSL_HANDSHAKING, ISSL_OPEN }; @@ -65,396 +60,406 @@ 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); -/** 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() + class Exception : public ModuleException { - outbound = false; - data_to_write = false; - } -}; - -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 ModuleSSLOpenSSL : public Module -{ - issl_session* sessions; - - SSL_CTX* ctx; - SSL_CTX* clictx; + public: + Exception(const std::string& reason) + : ModuleException(reason) { } + }; - std::string sslports; - bool use_sha; - - ServiceProvider iohook; - public: - - ModuleSSLOpenSSL() : iohook(this, "ssl/openssl", SERVICE_IOHOOK) + class DHParams { - sessions = new issl_session[ServerInstance->SE->GetMaxFds()]; + DH* dh; - /* Global SSL library initialization*/ - SSL_library_init(); - SSL_load_error_strings(); + public: + DHParams(const std::string& filename) + { + FILE* dhpfile = fopen(filename.c_str(), "r"); + if (dhpfile == NULL) + throw Exception("Couldn't open DH file " + filename + ": " + strerror(errno)); + + dh = PEM_read_DHparams(dhpfile, NULL, NULL, NULL); + fclose(dhpfile); + if (!dh) + throw Exception("Couldn't read DH params from file " + filename); + } - /* Build our SSL contexts: - * NOTE: OpenSSL makes us have two contexts, one for servers and one for clients. ICK. - */ - ctx = SSL_CTX_new( SSLv23_server_method() ); - clictx = SSL_CTX_new( SSLv23_client_method() ); + ~DHParams() + { + DH_free(dh); + } - 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); + DH* get() + { + return dh; + } + }; - 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); + class Context + { + SSL_CTX* const ctx; - const unsigned char session_id[] = "inspircd"; - SSL_CTX_set_session_id_context(ctx, session_id, sizeof(session_id) - 1); - } + public: + Context(SSL_CTX* context) + : ctx(context) + { + 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); - 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); - } + const unsigned char session_id[] = "inspircd"; + SSL_CTX_set_session_id_context(ctx, session_id, sizeof(session_id) - 1); + } - void OnHookIO(StreamSocket* user, ListenSocket* lsb) - { - if (!user->GetIOHook() && lsb->bind_tag->getString("ssl") == "openssl") + ~Context() { - /* Hook the user with our module */ - user->AddIOHook(this); + SSL_CTX_free(ctx); } - } - void OnRehash(User* user) - { - sslports.clear(); + bool SetDH(DHParams& dh) + { + return (SSL_CTX_set_tmp_dh(ctx, dh.get()) >= 0); + } - ConfigTag* Conf = ServerInstance->Config->ConfValue("openssl"); + bool SetCiphers(const std::string& ciphers) + { + return SSL_CTX_set_cipher_list(ctx, ciphers.c_str()); + } - if (Conf->getBool("showports", true)) + bool SetCerts(const std::string& filename) { - sslports = Conf->getString("advertisedports"); - if (!sslports.empty()) - return; + return SSL_CTX_use_certificate_chain_file(ctx, filename.c_str()); + } - for (size_t i = 0; i < ServerInstance->ports.size(); i++) - { - ListenSocket* port = ServerInstance->ports[i]; - if (port->bind_tag->getString("ssl") != "openssl") - continue; + bool SetPrivateKey(const std::string& filename) + { + return SSL_CTX_use_PrivateKey_file(ctx, filename.c_str(), SSL_FILETYPE_PEM); + } - 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()); + bool SetCA(const std::string& filename) + { + return SSL_CTX_load_verify_locations(ctx, filename.c_str(), 0); + } - if (port->bind_tag->getString("type", "clients") == "clients" && port->bind_addr != "127.0.0.1") - { - /* - * 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; - } - } + SSL* CreateSession() + { + return SSL_new(ctx); } - } + }; - void OnModuleRehash(User* user, const std::string ¶m) + class Profile : public refcountbase { - if (param != "ssl") - return; + /** Name of this profile + */ + const std::string name; - std::string keyfile; - std::string certfile; - std::string cafile; - std::string dhfile; - OnRehash(user); + /** DH parameters in use + */ + DHParams dh; - ConfigTag* conf = ServerInstance->Config->ConfValue("openssl"); + /** OpenSSL makes us have two contexts, one for servers and one for clients + */ + Context ctx; + Context clictx; - 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"); + /** Digest to use when generating fingerprints + */ + const EVP_MD* digest; - std::string ciphers = conf->getString("ciphers", ""); + /** Last error, set by error_callback() + */ + std::string lasterr; - if (!ciphers.empty()) + static int error_callback(const char* str, size_t len, void* u) { - if ((!SSL_CTX_set_cipher_list(ctx, ciphers.c_str())) || (!SSL_CTX_set_cipher_list(clictx, ciphers.c_str()))) - { - 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); - } + Profile* profile = reinterpret_cast<Profile*>(u); + profile->lasterr = std::string(str, len - 1); + return 0; } - /* Load our keys and certificates - * NOTE: OpenSSL's error logging API sucks, don't blame us for this clusterfuck. - */ - if ((!SSL_CTX_use_certificate_chain_file(ctx, certfile.c_str())) || (!SSL_CTX_use_certificate_chain_file(clictx, certfile.c_str()))) + 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())) { - 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); - } + if ((!ctx.SetDH(dh)) || (!clictx.SetDH(dh))) + throw Exception("Couldn't set DH parameters"); - 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); - } + std::string hash = tag->getString("hash", "md5"); + digest = EVP_get_digestbyname(hash.c_str()); + if (digest == NULL) + throw Exception("Unknown hash type " + hash); - /* Load the CAs we trust*/ - 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); - } + std::string ciphers = tag->getString("ciphers"); + if (!ciphers.empty()) + { + if ((!ctx.SetCiphers(ciphers)) || (!clictx.SetCiphers(ciphers))) + { + ERR_print_errors_cb(error_callback, this); + throw Exception("Can't set cipher list to \"" + ciphers + "\" " + lasterr); + } + } - FILE* dhpfile = fopen(dhfile.c_str(), "r"); - DH* ret; + /* 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); + } - 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)); - } - else - { - ret = PEM_read_DHparams(dhpfile, NULL, NULL, NULL); - if ((SSL_CTX_set_tmp_dh(ctx, ret) < 0) || (SSL_CTX_set_tmp_dh(clictx, ret) < 0)) + 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); + } + + // 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: Couldn't set DH parameters %s. SSL errors follow:", dhfile.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()); } - DH_free(ret); } - fclose(dhpfile); - } + const std::string& GetName() const { return name; } + SSL* CreateServerSession() { return ctx.CreateSession(); } + SSL* CreateClientSession() { return clictx.CreateSession(); } + const EVP_MD* GetDigest() { return digest; } + }; +} - void On005Numeric(std::string &output) - { - if (!sslports.empty()) - output.append(" SSL=" + sslports); - } +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); - ~ModuleSSLOpenSSL() - { - SSL_CTX_free(ctx); - SSL_CTX_free(clictx); - delete[] sessions; - } + SelfSigned = (ve == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT); + + return 1; +} + +class OpenSSLIOHook : public SSLIOHook +{ + private: + SSL* sess; + issl_status status; + const bool outbound; + bool data_to_write; + reference<OpenSSL::Profile> profile; - void OnUserConnect(LocalUser* user) + bool Handshake(StreamSocket* user) { - if (user->eh.GetIOHook() == this) + int ret; + + if (outbound) + ret = SSL_connect(sess); + else + ret = SSL_accept(sess); + + if (ret < 0) { - if (sessions[user->eh.GetFd()].sess) + int err = SSL_get_error(sess, ret); + + if (err == SSL_ERROR_WANT_READ) { - 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)); + 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(); } - } - } - void OnCleanup(int target_type, void* item) - { - if (target_type == TYPE_USER) + return false; + } + else if (ret > 0) { - LocalUser* user = IS_LOCAL((User*)item); + // Handshake complete. + VerifyCertificate(); - 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"); - } + status = ISSL_OPEN; + + SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE); + + return true; + } + else if (ret == 0) + { + CloseSession(); + return true; } - } - Version GetVersion() - { - return Version("Provides SSL support for clients", VF_VENDOR); + return true; } - void OnRequest(Request& request) + void CloseSession() { - if (strcmp("GET_SSL_CERT", request.id) == 0) + if (sess) { - SocketCertificateRequest& req = static_cast<SocketCertificateRequest&>(request); - int fd = req.sock->GetFd(); - issl_session* session = &sessions[fd]; - - req.cert = session->cert; + SSL_shutdown(sess); + SSL_free(sess); } + sess = NULL; + certificate = NULL; + status = ISSL_NONE; + errno = EIO; } - void OnStreamSocketAccept(StreamSocket* user, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) + void VerifyCertificate() { - int fd = user->GetFd(); - - issl_session* session = &sessions[fd]; - - session->sess = SSL_new(ctx); - session->status = ISSL_NONE; - session->outbound = false; - session->cert = NULL; + X509* cert; + ssl_cert* certinfo = new ssl_cert; + this->certificate = certinfo; + unsigned int n; + unsigned char md[EVP_MAX_MD_SIZE]; - if (session->sess == NULL) - return; + cert = SSL_get_peer_certificate(sess); - if (SSL_set_fd(session->sess, fd) == 0) + if (!cert) { - ServerInstance->Logs->Log("m_ssl_openssl",DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd); + certinfo->error = "Could not get peer certificate: "+std::string(get_error()); return; } - Handshake(user, session); - } - - void OnStreamSocketConnect(StreamSocket* user) - { - 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; + certinfo->invalid = (SSL_get_verify_result(sess) != X509_V_OK); - issl_session* session = &sessions[fd]; + if (!SelfSigned) + { + certinfo->unknownsigner = false; + certinfo->trusted = true; + } + else + { + certinfo->unknownsigner = true; + certinfo->trusted = false; + } - session->sess = SSL_new(clictx); - session->status = ISSL_NONE; - session->outbound = true; + char buf[512]; + X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)); + certinfo->dn = buf; + X509_NAME_oneline(X509_get_issuer_name(cert), buf, sizeof(buf)); + certinfo->issuer = buf; - 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 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]); + 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) { 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); 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; } } @@ -463,68 +468,64 @@ class ModuleSSLOpenSSL : public Module 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) { - int ret = SSL_write(session->sess, buffer.data(), buffer.size()); + int ret = SSL_write(sess, buffer.data(), buffer.size()); 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; } } @@ -532,142 +533,159 @@ class ModuleSSLOpenSSL : public Module 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 fingerprint is " + fingerprint; - if (session->outbound) - ret = SSL_connect(session->sess); - else - ret = SSL_accept(session->sess); + user->WriteNotice(text); + } + } +}; - if (ret < 0) +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) - { - ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE); - session->status = ISSL_HANDSHAKING; - return true; - } - else if (err == SSL_ERROR_WANT_WRITE) + try { - ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE); - session->status = ISSL_HANDSHAKING; - return true; + reference<OpenSSL::Profile> profile(new OpenSSL::Profile(defname, tag)); + newprofiles.push_back(new OpenSSLIOHookProvider(this, profile)); } - else + catch (OpenSSL::Exception& ex) { - 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); - return true; + newprofiles.push_back(new OpenSSLIOHookProvider(this, profile)); } - return true; + 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; - errno = EIO; + // 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; - - 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; - X509_NAME_oneline(X509_get_issuer_name(cert), buf, sizeof(buf)); - certinfo->issuer = buf; + 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) diff --git a/src/modules/m_abbreviation.cpp b/src/modules/m_abbreviation.cpp index a744f55f6..5fa0f55fc 100644 --- a/src/modules/m_abbreviation.cpp +++ b/src/modules/m_abbreviation.cpp @@ -19,27 +19,20 @@ #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() != '.') @@ -61,7 +54,7 @@ class ModuleAbbreviation : public Module { 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; } @@ -79,7 +72,7 @@ class ModuleAbbreviation : public Module /* 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; } diff --git a/src/modules/m_alias.cpp b/src/modules/m_alias.cpp index 32fc80b64..9055a8bf8 100644 --- a/src/modules/m_alias.cpp +++ b/src/modules/m_alias.cpp @@ -22,15 +22,13 @@ #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; @@ -59,20 +57,22 @@ class Alias 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); @@ -85,8 +85,8 @@ class ModuleAlias : public Module { 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"); @@ -99,20 +99,12 @@ class ModuleAlias : public Module } } - 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); } @@ -142,10 +134,8 @@ class ModuleAlias : public Module 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. */ @@ -153,19 +143,16 @@ class ModuleAlias : public Module 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) { @@ -174,29 +161,27 @@ class ModuleAlias : public Module 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; } @@ -207,37 +192,31 @@ class ModuleAlias : public Module // 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) { @@ -245,16 +224,12 @@ class ModuleAlias : public Module 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()) { @@ -270,24 +245,22 @@ class ModuleAlias : public Module } } - 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; } } @@ -316,7 +289,7 @@ class ModuleAlias : public Module 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]; @@ -367,19 +340,14 @@ class ModuleAlias : public Module 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); } - 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"); diff --git a/src/modules/m_allowinvite.cpp b/src/modules/m_allowinvite.cpp index 08a5f542a..05e76113a 100644 --- a/src/modules/m_allowinvite.cpp +++ b/src/modules/m_allowinvite.cpp @@ -19,8 +19,6 @@ #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: @@ -36,19 +34,12 @@ class ModuleAllowInvite : public Module { } - 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)) { @@ -56,10 +47,10 @@ class ModuleAllowInvite : public Module 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; @@ -69,11 +60,7 @@ class ModuleAllowInvite : public Module 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); } diff --git a/src/modules/m_alltime.cpp b/src/modules/m_alltime.cpp index 38ae4b254..58f7c4fb5 100644 --- a/src/modules/m_alltime.cpp +++ b/src/modules/m_alltime.cpp @@ -21,15 +21,12 @@ #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) @@ -52,7 +49,6 @@ class CommandAlltime : public Command } }; - class Modulealltime : public Module { CommandAlltime mycommand; @@ -62,16 +58,7 @@ class Modulealltime : public Module { } - 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); } diff --git a/src/modules/m_auditorium.cpp b/src/modules/m_auditorium.cpp index c3e31c583..8ec80ef46 100644 --- a/src/modules/m_auditorium.cpp +++ b/src/modules/m_auditorium.cpp @@ -22,55 +22,28 @@ #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"); @@ -78,7 +51,7 @@ class ModuleAuditorium : public Module 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); } @@ -112,7 +85,7 @@ class ModuleAuditorium : public Module return false; } - void OnNamesListItem(User* issuer, Membership* memb, std::string &prefixes, std::string &nick) + void 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()) @@ -141,34 +114,36 @@ class ModuleAuditorium : public Module } } - 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(); + const UserMembList* users = memb->chan->GetUsers(); for(UserMembCIter j = users->begin(); j != users->end(); j++) { if (IS_LOCAL(j->first) && CanSee(j->first, memb)) @@ -177,12 +152,10 @@ class ModuleAuditorium : public Module } } - 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 (IsVisible(memb)) return; if (CanSee(source, memb)) diff --git a/src/modules/m_autoop.cpp b/src/modules/m_autoop.cpp index 07dca6929..828bef14c 100644 --- a/src/modules/m_autoop.cpp +++ b/src/modules/m_autoop.cpp @@ -19,9 +19,7 @@ #include "inspircd.h" -#include "u_listmode.h" - -/* $ModDesc: Provides support for the +w channel mode, autoop list */ +#include "listmode.h" /** Handles +w channel mode */ @@ -34,17 +32,13 @@ class AutoOpList : public ListModeBase 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) @@ -54,12 +48,12 @@ class AutoOpList : public ListModeBase 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) @@ -70,8 +64,8 @@ class AutoOpList : public ListModeBase 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; @@ -82,40 +76,31 @@ class ModuleAutoOp : public Module { 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++) + 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()) + PrefixMode* given = mh.FindMode(it->mask.substr(0, colon)); + if (given) modeline.push_back(given->GetModeChar()); } } @@ -123,21 +108,16 @@ public: 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(modechange, ServerInstance->FakeClient); } } - 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); } diff --git a/src/modules/m_banexception.cpp b/src/modules/m_banexception.cpp index 1811f743d..b29b39747 100644 --- a/src/modules/m_banexception.cpp +++ b/src/modules/m_banexception.cpp @@ -22,10 +22,7 @@ #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 */ @@ -49,85 +46,63 @@ class ModuleBanException : public Module { 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); } diff --git a/src/modules/m_banredirect.cpp b/src/modules/m_banredirect.cpp index 2e2592541..1a123e580 100644 --- a/src/modules/m_banredirect.cpp +++ b/src/modules/m_banredirect.cpp @@ -23,9 +23,7 @@ #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 */ @@ -43,18 +41,20 @@ class BanRedirectEntry }; 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 @@ -63,21 +63,23 @@ class BanRedirect : public ModeWatcher * 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(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; } @@ -86,23 +88,25 @@ class BanRedirect : public ModeWatcher 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; } } @@ -133,27 +137,27 @@ class BanRedirect : public ModeWatcher { 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; } } @@ -213,26 +217,19 @@ class ModuleBanRedirect : public Module { BanRedirect re; bool nofollow; + ChanModeReference limitmode; + ChanModeReference redirectmode; public: ModuleBanRedirect() - : re(this) - { - nofollow = false; - } - - - void init() + : re(this) + , nofollow(false) + , limitmode(this, "limit") + , redirectmode(this, "redirect") { - 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) { @@ -242,9 +239,6 @@ class ModuleBanRedirect : public Module if(redirects) { irc::modestacker modestack(false); - StringDeque stackresult; - std::vector<std::string> mode_junk; - mode_junk.push_back(chan->name); for(BanRedirectList::iterator i = redirects->begin(); i != redirects->end(); i++) { @@ -257,17 +251,18 @@ class ModuleBanRedirect : public Module modestack.Push('b', i->banmask); } - while(modestack.GetStackedLine(stackresult)) + std::vector<std::string> stackresult; + stackresult.push_back(chan->name); + 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(stackresult, ServerInstance->FakeClient, ModeParser::MODE_LOCALONLY); + stackresult.erase(stackresult.begin() + 1, stackresult.end()); } } } } - 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) { @@ -311,19 +306,19 @@ class ModuleBanRedirect : public Module 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; } @@ -334,14 +329,7 @@ class ModuleBanRedirect : public Module 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); } diff --git a/src/modules/m_blockamsg.cpp b/src/modules/m_blockamsg.cpp index be861447f..833828233 100644 --- a/src/modules/m_blockamsg.cpp +++ b/src/modules/m_blockamsg.cpp @@ -23,8 +23,6 @@ #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. @@ -37,7 +35,7 @@ enum BlockAction { IBLOCK_KILL, IBLOCK_KILLOPERS, IBLOCK_NOTICE, IBLOCK_NOTICEOP */ class BlockedMessage { -public: + public: std::string message; irc::string target; time_t sent; @@ -59,24 +57,12 @@ class ModuleBlockAmsg : public Module { } - 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); @@ -94,7 +80,7 @@ class ModuleBlockAmsg : public Module 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) @@ -169,5 +155,4 @@ class ModuleBlockAmsg : public Module } }; - MODULE_INIT(ModuleBlockAmsg) diff --git a/src/modules/m_blockcaps.cpp b/src/modules/m_blockcaps.cpp index 200693699..f73101e88 100644 --- a/src/modules/m_blockcaps.cpp +++ b/src/modules/m_blockcaps.cpp @@ -22,8 +22,6 @@ #include "inspircd.h" -/* $ModDesc: Provides support to block all-CAPS channel messages and notices */ - /** Handles the +B channel mode */ @@ -39,31 +37,18 @@ class ModuleBlockCAPS : public Module 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) { @@ -76,7 +61,7 @@ public: 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 "; @@ -97,7 +82,7 @@ public: } if ( ((caps*100)/(int)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; } } @@ -105,37 +90,18 @@ public: 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); } diff --git a/src/modules/m_blockcolor.cpp b/src/modules/m_blockcolor.cpp index 3cc01b4c0..a08ad7c6f 100644 --- a/src/modules/m_blockcolor.cpp +++ b/src/modules/m_blockcolor.cpp @@ -23,8 +23,6 @@ #include "inspircd.h" -/* $ModDesc: Provides channel mode +c to block color */ - /** Handles the +c channel mode */ class BlockColor : public SimpleChannelModeHandler @@ -42,19 +40,12 @@ class ModuleBlockColor : public Module { } - 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))) { @@ -64,7 +55,7 @@ class ModuleBlockColor : public Module 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++) { @@ -76,7 +67,7 @@ class ModuleBlockColor : public Module 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; } @@ -86,16 +77,7 @@ class ModuleBlockColor : public Module 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); } diff --git a/src/modules/m_botmode.cpp b/src/modules/m_botmode.cpp index b29c58240..67f692b86 100644 --- a/src/modules/m_botmode.cpp +++ b/src/modules/m_botmode.cpp @@ -21,8 +21,6 @@ #include "inspircd.h" -/* $ModDesc: Provides user mode +B to mark the user as a bot */ - /** Handles user mode +B */ class BotMode : public SimpleUserModeHandler @@ -40,31 +38,18 @@ class ModuleBotMode : public Module { } - 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) diff --git a/src/modules/m_callerid.cpp b/src/modules/m_callerid.cpp index 09c5c3f24..6f2c67300 100644 --- a/src/modules/m_callerid.cpp +++ b/src/modules/m_callerid.cpp @@ -22,7 +22,17 @@ #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 { @@ -62,12 +72,20 @@ struct CallerIDExtInfo : public ExtensionItem 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; @@ -76,9 +94,6 @@ struct CallerIDExtInfo : public ExtensionItem while (s.GetToken(tok)) { - if (tok.empty()) - continue; - User *u = ServerInstance->FindNick(tok); if ((u) && (u->registered == REG_ALL) && (!u->quitting) && (!IS_SERVER(u))) { @@ -117,7 +132,7 @@ struct CallerIDExtInfo : public ExtensionItem 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. } @@ -125,7 +140,7 @@ struct CallerIDExtInfo : public ExtensionItem 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; } @@ -139,6 +154,28 @@ public: 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; @@ -147,42 +184,21 @@ public: { allow_empty_last_param = false; syntax = "{[+|-]<nicks>}|*}"; - 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. @@ -192,54 +208,58 @@ public: */ 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) @@ -248,9 +268,9 @@ public: 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) @@ -259,12 +279,12 @@ public: 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; } @@ -272,7 +292,7 @@ public: 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; } @@ -282,13 +302,13 @@ public: 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; } @@ -299,7 +319,7 @@ public: 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; } @@ -308,17 +328,16 @@ public: // 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; @@ -348,7 +367,7 @@ private: 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(); @@ -359,38 +378,26 @@ public: { } - 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); @@ -400,12 +407,12 @@ public: { 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; @@ -413,34 +420,18 @@ public: 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); @@ -451,5 +442,3 @@ public: }; MODULE_INIT(ModuleCallerID) - - diff --git a/src/modules/m_cap.cpp b/src/modules/m_cap.cpp index e9f4dae90..bc79e59ec 100644 --- a/src/modules/m_cap.cpp +++ b/src/modules/m_cap.cpp @@ -19,9 +19,7 @@ #include "inspircd.h" -#include "m_cap.h" - -/* $ModDesc: Provides the CAP negotiation mechanism seen in ratbox-derived ircds */ +#include "modules/cap.h" /* CAP LS @@ -51,7 +49,8 @@ class CommandCAP : public Command 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") { @@ -66,6 +65,7 @@ class CommandCAP : public Command while (cap_stream.GetToken(cap_)) { + std::transform(cap_.begin(), cap_.end(), cap_.begin(), ::tolower); Data.wanted.push_back(cap_); } @@ -74,14 +74,14 @@ class CommandCAP : public Command 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") @@ -95,11 +95,8 @@ class CommandCAP : public Command 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") { @@ -108,14 +105,12 @@ class CommandCAP : public Command 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; } @@ -132,16 +127,7 @@ class ModuleCAP : public Module { } - 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)) @@ -150,15 +136,10 @@ class ModuleCAP : public Module return MOD_RES_PASSTHRU; } - ~ModuleCAP() - { - } - - Version GetVersion() + Version GetVersion() CXX11_OVERRIDE { return Version("Client CAP extension support", VF_VENDOR); } }; MODULE_INIT(ModuleCAP) - diff --git a/src/modules/m_cban.cpp b/src/modules/m_cban.cpp index fb78e41b2..4fb0653a9 100644 --- a/src/modules/m_cban.cpp +++ b/src/modules/m_cban.cpp @@ -23,25 +23,22 @@ #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) { @@ -55,15 +52,9 @@ public: 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; } }; @@ -95,7 +86,6 @@ class CommandCBan : public Command 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) @@ -111,14 +101,14 @@ class CommandCBan : public Command } 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()); @@ -131,14 +121,14 @@ class CommandCBan : public Command 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; } } @@ -164,22 +154,18 @@ class ModuleCBan : public Module { } - 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; @@ -188,27 +174,26 @@ class ModuleCBan : public Module 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) - diff --git a/src/modules/m_censor.cpp b/src/modules/m_censor.cpp index 50c8e22a7..209d61d4a 100644 --- a/src/modules/m_censor.cpp +++ b/src/modules/m_censor.cpp @@ -20,13 +20,7 @@ */ -/* $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; @@ -55,24 +49,8 @@ class ModuleCensor : public Module 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; @@ -80,11 +58,11 @@ class ModuleCensor : public Module 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) @@ -101,7 +79,7 @@ class ModuleCensor : public Module { 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; } @@ -112,12 +90,7 @@ class ModuleCensor : public Module 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 @@ -136,7 +109,7 @@ class ModuleCensor : public Module } } - virtual Version GetVersion() + Version GetVersion() CXX11_OVERRIDE { return Version("Provides user and channel +G mode",VF_VENDOR); } diff --git a/src/modules/m_cgiirc.cpp b/src/modules/m_cgiirc.cpp index ccbaaae32..0f60995a3 100644 --- a/src/modules/m_cgiirc.cpp +++ b/src/modules/m_cgiirc.cpp @@ -25,8 +25,7 @@ #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 }; @@ -90,11 +89,11 @@ class CommandWebirc : public Command realhost.set(user, user->host); realip.set(user, user->GetIPString()); - bool host_ok = (parameters[2].length() < 64); + 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) @@ -108,7 +107,7 @@ class CommandWebirc : public Command } } - 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; } }; @@ -116,39 +115,44 @@ class CommandWebirc : public Command /** 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; @@ -156,11 +160,11 @@ class CGIResolver : public Resolver 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) @@ -175,6 +179,7 @@ class ModuleCgiIRC : public Module { CommandWebirc cmd; LocalIntExt waiting; + dynamic_reference<DNS::Manager> DNS; static void RecheckClass(LocalUser* user) { @@ -187,8 +192,7 @@ class ModuleCgiIRC : public Module { ServerInstance->Users->RemoveCloneCounts(user); user->SetClientIP(newip.c_str()); - ServerInstance->Users->AddLocalClone(user); - ServerInstance->Users->AddGlobalClone(user); + ServerInstance->Users->AddClone(user); } void HandleIdentOrPass(LocalUser* user, const std::string& newip, bool was_pass) @@ -199,40 +203,42 @@ class ModuleCgiIRC : public Module 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(); @@ -251,7 +257,7 @@ public: { 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 { @@ -267,7 +273,7 @@ public: 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)); @@ -275,13 +281,13 @@ public: } 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; @@ -309,7 +315,7 @@ public: 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++) { @@ -387,7 +393,7 @@ public: 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++) @@ -406,11 +412,10 @@ public: 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) diff --git a/src/modules/m_chancreate.cpp b/src/modules/m_chancreate.cpp index 997a92648..6cf4af235 100644 --- a/src/modules/m_chancreate.cpp +++ b/src/modules/m_chancreate.cpp @@ -21,26 +21,20 @@ #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))) { diff --git a/src/modules/m_chanfilter.cpp b/src/modules/m_chanfilter.cpp index 651e659b5..53428a5a8 100644 --- a/src/modules/m_chanfilter.cpp +++ b/src/modules/m_chanfilter.cpp @@ -23,13 +23,8 @@ */ -/* $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 */ @@ -38,31 +33,30 @@ class ChanFilter : public ListModeBase 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()); } }; @@ -78,42 +72,35 @@ class ModuleChanFilter : public Module { } - 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; } } @@ -122,33 +109,10 @@ class ModuleChanFilter : public Module 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) diff --git a/src/modules/m_chanhistory.cpp b/src/modules/m_chanhistory.cpp index e48e67fe5..f6e7ea40e 100644 --- a/src/modules/m_chanhistory.cpp +++ b/src/modules/m_chanhistory.cpp @@ -19,8 +19,6 @@ #include "inspircd.h" -/* $ModDesc: Provides channel history for a given number of lines */ - struct HistoryItem { time_t ts; @@ -32,10 +30,13 @@ struct HistoryList { 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) { @@ -52,110 +53,98 @@ class HistoryMode : public ModeHandler } 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; @@ -165,8 +154,7 @@ class ModuleChanHistory : public Module 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) @@ -176,7 +164,7 @@ class ModuleChanHistory : public Module } } - Version GetVersion() + Version GetVersion() CXX11_OVERRIDE { return Version("Provides channel history replayed on join", VF_VENDOR); } diff --git a/src/modules/m_chanlog.cpp b/src/modules/m_chanlog.cpp index 6dbc0e7a8..736285be8 100644 --- a/src/modules/m_chanlog.cpp +++ b/src/modules/m_chanlog.cpp @@ -20,11 +20,8 @@ #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. */ @@ -32,19 +29,7 @@ class ModuleChanLog : public Module 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; @@ -59,100 +44,44 @@ class ModuleChanLog : public Module 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 - diff --git a/src/modules/m_channames.cpp b/src/modules/m_channames.cpp index 325e8fee1..69b501792 100644 --- a/src/modules/m_channames.cpp +++ b/src/modules/m_channames.cpp @@ -19,74 +19,70 @@ #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() { badchan = true; std::vector<Channel*> chanvec; - for (chan_hash::const_iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); ++i) + const chan_hash& chans = ServerInstance->GetChans(); + for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i) { - if (!ServerInstance->IsChannel(i->second->name.c_str(), MAXBUF)) + if (!ServerInstance->IsChannel(i->second->name)) chanvec.push_back(i->second); } std::vector<Channel*>::reverse_iterator c2 = chanvec.rbegin(); while (c2 != chanvec.rend()) { Channel* c = *c2++; - if (c->IsModeSet('P') && c->GetUserCounter()) + if (c->IsModeSet(permchannelmode) && c->GetUserCounter()) { std::vector<std::string> modes; modes.push_back(c->name); - modes.push_back("-P"); + modes.push_back(std::string("-") + permchannelmode->GetModeChar()); - ServerInstance->SendGlobalMode(modes, ServerInstance->FakeClient); + ServerInstance->Modes->Process(modes, ServerInstance->FakeClient); } const UserMembList* users = c->GetUsers(); for(UserMembCIter j = users->begin(); j != users->end(); ) @@ -104,7 +100,7 @@ class ModuleChannelNames : public Module 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"); @@ -134,7 +130,7 @@ class ModuleChannelNames : public Module 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) { @@ -145,13 +141,13 @@ class ModuleChannelNames : public Module } } - virtual ~ModuleChannelNames() + ~ModuleChannelNames() { ServerInstance->IsChannel = rememberer; ValidateChans(); } - virtual Version GetVersion() + Version GetVersion() CXX11_OVERRIDE { return Version("Implements config tags which allow changing characters allowed in channel names", VF_VENDOR); } diff --git a/src/modules/m_channelban.cpp b/src/modules/m_channelban.cpp index 6eec486ea..300caa123 100644 --- a/src/modules/m_channelban.cpp +++ b/src/modules/m_channelban.cpp @@ -20,28 +20,15 @@ #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] == ':')) { @@ -55,15 +42,9 @@ class ModuleBadChannelExtban : public Module } for (UCListIter 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; } } @@ -71,12 +52,10 @@ class ModuleBadChannelExtban : public Module 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) - diff --git a/src/modules/m_chanprotect.cpp b/src/modules/m_chanprotect.cpp deleted file mode 100644 index affd0c8d6..000000000 --- a/src/modules/m_chanprotect.cpp +++ /dev/null @@ -1,308 +0,0 @@ -/* - * 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) diff --git a/src/modules/m_check.cpp b/src/modules/m_check.cpp index 07276445f..35901f8d5 100644 --- a/src/modules/m_check.cpp +++ b/src/modules/m_check.cpp @@ -20,16 +20,51 @@ */ -/* $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>"; } @@ -90,26 +125,26 @@ class CommandCheck : public Command 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; @@ -145,8 +180,8 @@ class CommandCheck : public Command 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()) @@ -157,8 +192,12 @@ class CommandCheck : public Command for (UCListIter 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); @@ -190,27 +229,20 @@ class CommandCheck : public Command /* note that unlike /names, we do NOT check +i vs in the channel */ for (UserMembCIter 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 @@ -219,7 +251,8 @@ class CommandCheck : public Command 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)) { @@ -250,42 +283,15 @@ class CommandCheck : public Command } }; - 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); } diff --git a/src/modules/m_chghost.cpp b/src/modules/m_chghost.cpp index 08f7f76fa..3a637f9d0 100644 --- a/src/modules/m_chghost.cpp +++ b/src/modules/m_chghost.cpp @@ -21,13 +21,10 @@ #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) @@ -35,16 +32,16 @@ class CommandChghost : public Command 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; } @@ -52,7 +49,7 @@ class CommandChghost : public Command { 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; } } @@ -61,13 +58,13 @@ class CommandChghost : public Command 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 (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); @@ -91,20 +88,13 @@ class ModuleChgHost : public Module { 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"); @@ -113,15 +103,10 @@ class ModuleChgHost : public Module 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) diff --git a/src/modules/m_chgident.cpp b/src/modules/m_chgident.cpp index 2112e45a3..c855216bf 100644 --- a/src/modules/m_chgident.cpp +++ b/src/modules/m_chgident.cpp @@ -22,8 +22,6 @@ #include "inspircd.h" -/* $ModDesc: Provides support for the CHGIDENT command */ - /** Handle /CHGIDENT */ class CommandChgident : public Command @@ -34,7 +32,7 @@ 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) @@ -43,27 +41,27 @@ class CommandChgident : public Command 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()); } @@ -79,7 +77,6 @@ class CommandChgident : public Command } }; - class ModuleChgIdent : public Module { CommandChgident cmd; @@ -89,21 +86,10 @@ public: { } - 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) - diff --git a/src/modules/m_chgname.cpp b/src/modules/m_chgname.cpp index 73ae3d487..830d5070b 100644 --- a/src/modules/m_chgname.cpp +++ b/src/modules/m_chgname.cpp @@ -20,8 +20,6 @@ #include "inspircd.h" -/* $ModDesc: Provides support for the CHGNAME command */ - /** Handle /CHGNAME */ class CommandChgname : public Command @@ -32,7 +30,7 @@ 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) @@ -41,25 +39,25 @@ class CommandChgname : public Command 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()); } @@ -75,7 +73,6 @@ class CommandChgname : public Command } }; - class ModuleChgName : public Module { CommandChgname cmd; @@ -85,20 +82,10 @@ public: { } - 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) diff --git a/src/modules/m_clearchan.cpp b/src/modules/m_clearchan.cpp new file mode 100644 index 000000000..27f8ec32f --- /dev/null +++ b/src/modules/m_clearchan.cpp @@ -0,0 +1,217 @@ +/* + * 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 + const UserMembList* users = chan->GetUsers(); + for (UserMembCIter i = users->begin(); i != users->end(); ) + { + User* curr = i->first; + ++i; + + if (!IS_LOCAL(curr) || curr->IsOper()) + continue; + + // If kicking users, remove them and skip the QuitUser() + if (kick) + { + chan->KickUser(ServerInstance->FakeClient, curr, 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 UserMembList* users = cmd.activechan->GetUsers(); + for (UserMembCIter 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 UserMembList* users = memb->chan->GetUsers(); + for (UserMembCIter 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) diff --git a/src/modules/m_cloaking.cpp b/src/modules/m_cloaking.cpp index 105d68833..5d62c9cf6 100644 --- a/src/modules/m_cloaking.cpp +++ b/src/modules/m_cloaking.cpp @@ -24,16 +24,10 @@ #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 */ @@ -49,7 +43,6 @@ class CloakUser : public ModeHandler { public: LocalStringExt ext; - std::string debounce_uid; time_t debounce_ts; int debounce_count; @@ -70,7 +63,7 @@ class CloakUser : public ModeHandler */ if (!user) { - dest->SetMode('x',adding); + dest->SetMode(this, adding); return MODEACTION_ALLOW; } @@ -87,7 +80,7 @@ class CloakUser : public ModeHandler 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 */ @@ -106,8 +99,8 @@ class CloakUser : public ModeHandler } if (cloak) { - user->ChangeDisplayedHost(cloak->c_str()); - user->SetMode('x',true); + user->ChangeDisplayedHost(*cloak); + user->SetMode(this, true); return MODEACTION_ALLOW; } else @@ -118,12 +111,11 @@ class CloakUser : public ModeHandler /* 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 @@ -147,7 +139,6 @@ class ModuleCloaking : public Module std::string prefix; std::string suffix; std::string key; - unsigned int compatkey[4]; const char* xtab[4]; dynamic_reference<HashProvider> Hash; @@ -155,18 +146,6 @@ class ModuleCloaking : public Module { } - 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. * @@ -224,63 +203,6 @@ class ModuleCloaking : public Module 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; @@ -348,7 +270,7 @@ class ModuleCloaking : public Module 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) @@ -359,9 +281,8 @@ class ModuleCloaking : public Module /* 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; @@ -375,32 +296,22 @@ class ModuleCloaking : public Module // 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; @@ -411,82 +322,23 @@ class ModuleCloaking : public Module 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) @@ -495,29 +347,6 @@ class ModuleCloaking : public Module 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) @@ -533,7 +362,7 @@ class ModuleCloaking : public Module return chost; } - void OnUserConnect(LocalUser* dest) + void OnUserConnect(LocalUser* dest) CXX11_OVERRIDE { std::string* cloak = cu.ext.get(dest); if (cloak) @@ -554,7 +383,7 @@ CmdResult CommandCloak::Handle(const std::vector<std::string> ¶meters, User 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; } diff --git a/src/modules/m_clones.cpp b/src/modules/m_clones.cpp index 92b1bda78..c51c8d3b4 100644 --- a/src/modules/m_clones.cpp +++ b/src/modules/m_clones.cpp @@ -21,8 +21,6 @@ #include "inspircd.h" -/* $ModDesc: Provides the /CLONES command to retrieve information on clones. */ - /** Handle /CLONES */ class CommandClones : public Command @@ -50,11 +48,12 @@ 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"); @@ -63,31 +62,18 @@ class CommandClones : public Command } }; - 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) diff --git a/src/modules/m_close.cpp b/src/modules/m_close.cpp index 8b0ea3417..9c5c9a77b 100644 --- a/src/modules/m_close.cpp +++ b/src/modules/m_close.cpp @@ -20,8 +20,6 @@ #include "inspircd.h" -/* $ModDesc: Provides /CLOSE functionality */ - /** Handle /CLOSE */ class CommandClose : public Command @@ -30,7 +28,8 @@ 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) { @@ -50,13 +49,14 @@ class CommandClose : public Command 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; } @@ -71,16 +71,7 @@ class ModuleClose : public Module { } - void init() - { - ServerInstance->Modules->AddService(cmd); - } - - virtual ~ModuleClose() - { - } - - virtual Version GetVersion() + Version GetVersion() CXX11_OVERRIDE { return Version("Provides /CLOSE functionality", VF_VENDOR); } diff --git a/src/modules/m_commonchans.cpp b/src/modules/m_commonchans.cpp index afa17add4..eab53b9bc 100644 --- a/src/modules/m_commonchans.cpp +++ b/src/modules/m_commonchans.cpp @@ -19,8 +19,6 @@ #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 @@ -37,41 +35,24 @@ class ModulePrivacyMode : public Module { } - 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) diff --git a/src/modules/m_conn_join.cpp b/src/modules/m_conn_join.cpp index 6b13ab1aa..631e5945c 100644 --- a/src/modules/m_conn_join.cpp +++ b/src/modules/m_conn_join.cpp @@ -22,45 +22,93 @@ #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, ServerInstance->Time(), 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) diff --git a/src/modules/m_conn_umodes.cpp b/src/modules/m_conn_umodes.cpp index a21462ddf..1e3ea1a49 100644 --- a/src/modules/m_conn_umodes.cpp +++ b/src/modules/m_conn_umodes.cpp @@ -22,32 +22,21 @@ #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]; @@ -62,26 +51,14 @@ class ModuleModesOnConnect : public Module 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->Modes->Process(modes, user); } memcpy(ServerInstance->Config->DisabledUModes, save, 64); diff --git a/src/modules/m_conn_waitpong.cpp b/src/modules/m_conn_waitpong.cpp index 1d48220a6..496b04c2d 100644 --- a/src/modules/m_conn_waitpong.cpp +++ b/src/modules/m_conn_waitpong.cpp @@ -24,8 +24,6 @@ #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; @@ -38,35 +36,27 @@ class ModuleWaitPong : public Module { } - 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") { @@ -90,20 +80,15 @@ class ModuleWaitPong : public Module 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) diff --git a/src/modules/m_connectban.cpp b/src/modules/m_connectban.cpp index 26120add9..fcb4b09ed 100644 --- a/src/modules/m_connectban.cpp +++ b/src/modules/m_connectban.cpp @@ -20,61 +20,39 @@ #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) { @@ -87,7 +65,7 @@ class ModuleConnectBan : public Module } irc::sockets::cidr_mask mask(u->client_sa, range); - i = connects.find(mask); + ConnectMap::iterator i = connects.find(mask); if (i != connects.end()) { @@ -96,7 +74,7 @@ class ModuleConnectBan : public Module 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; @@ -104,7 +82,7 @@ class ModuleConnectBan : public Module } 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); @@ -117,9 +95,9 @@ class ModuleConnectBan : public Module } } - virtual void OnGarbageCollect() + void OnGarbageCollect() { - ServerInstance->Logs->Log("m_connectban",DEBUG, "Clearing map."); + ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Clearing map."); connects.clear(); } }; diff --git a/src/modules/m_connflood.cpp b/src/modules/m_connflood.cpp index f77691e32..2ab906e27 100644 --- a/src/modules/m_connflood.cpp +++ b/src/modules/m_connflood.cpp @@ -21,11 +21,8 @@ #include "inspircd.h" -/* $ModDesc: Connection throttle */ - class ModuleConnFlood : public Module { -private: int seconds, timeout, boot_wait; unsigned int conns; unsigned int maxconns; @@ -39,19 +36,12 @@ public: { } - 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"); @@ -67,7 +57,7 @@ public: first = ServerInstance->Time(); } - virtual ModResult OnUserRegister(LocalUser* user) + ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE { if (user->exempt) return MOD_RES_PASSTHRU; @@ -114,12 +104,6 @@ public: } return MOD_RES_PASSTHRU; } - - virtual void OnRehash(User* user) - { - InitConf(); - } - }; MODULE_INIT(ModuleConnFlood) diff --git a/src/modules/m_customprefix.cpp b/src/modules/m_customprefix.cpp index dfc60e082..65c2cbd31 100644 --- a/src/modules/m_customprefix.cpp +++ b/src/modules/m_customprefix.cpp @@ -19,89 +19,38 @@ #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(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); + prefixrank = tag->getInt("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) @@ -110,7 +59,7 @@ class ModuleCustomPrefix : public Module 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()); @@ -120,18 +69,17 @@ class ModuleCustomPrefix : public Module } 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); } diff --git a/src/modules/m_customtitle.cpp b/src/modules/m_customtitle.cpp index c65645bc9..3386e8cd7 100644 --- a/src/modules/m_customtitle.cpp +++ b/src/modules/m_customtitle.cpp @@ -21,8 +21,6 @@ #include "inspircd.h" -/* $ModDesc: Provides the TITLE command which allows setting of CUSTOM WHOIS TITLE line */ - /** Handle /TITLE */ class CommandTitle : public Command @@ -35,27 +33,10 @@ 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) @@ -67,22 +48,23 @@ class CommandTitle : public Command 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; } @@ -97,15 +79,8 @@ class ModuleCustomTitle : public Module { } - 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) @@ -114,18 +89,14 @@ class ModuleCustomTitle : public Module 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); } diff --git a/src/modules/m_cycle.cpp b/src/modules/m_cycle.cpp index 383e7b5a2..c8b6bd8b4 100644 --- a/src/modules/m_cycle.cpp +++ b/src/modules/m_cycle.cpp @@ -20,23 +20,21 @@ #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) { @@ -46,34 +44,27 @@ class CommandCycle : public Command 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; @@ -84,26 +75,17 @@ class CommandCycle : public Command 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) diff --git a/src/modules/m_dccallow.cpp b/src/modules/m_dccallow.cpp index db0b54764..7332402ba 100644 --- a/src/modules/m_dccallow.cpp +++ b/src/modules/m_dccallow.cpp @@ -25,8 +25,6 @@ #include "inspircd.h" -/* $ModDesc: Provides support for the /DCCALLOW command */ - class BannedFileList { public: @@ -53,12 +51,16 @@ typedef std::vector<DCCAllow> dccallowlist; 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>|HELP|LIST}"; /* XXX we need to fix this so it can work with translation stuff (i.e. move +- into a seperate param */ @@ -94,7 +96,7 @@ class CommandDccallow : public Command } 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; } } @@ -108,7 +110,7 @@ class CommandDccallow : public Command 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) @@ -117,7 +119,7 @@ class CommandDccallow : public Command 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; } } @@ -127,15 +129,15 @@ class CommandDccallow : public Command { 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); } @@ -144,7 +146,7 @@ class CommandDccallow : public Command { 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; } } @@ -152,10 +154,10 @@ class CommandDccallow : public Command 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())) { @@ -163,10 +165,10 @@ class CommandDccallow : public Command } else { - length = ServerInstance->Duration(parameters[1]); + length = InspIRCd::Duration(parameters[1]); } - if (!ServerInstance->IsValidMask(mask)) + if (!InspIRCd::IsValidMask(mask)) { return CMD_FAILURE; } @@ -175,11 +177,11 @@ class CommandDccallow : public Command 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. */ @@ -189,7 +191,7 @@ class CommandDccallow : public Command 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; } } @@ -203,26 +205,26 @@ class CommandDccallow : public Command 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) @@ -232,51 +234,37 @@ class CommandDccallow : public Command 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) @@ -291,17 +279,12 @@ class ModuleDCCAllow : public Module 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; @@ -323,7 +306,7 @@ class ModuleDCCAllow : public Module 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) @@ -369,16 +352,16 @@ class ModuleDCCAllow : public Module 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; } } @@ -392,7 +375,7 @@ class ModuleDCCAllow : public Module 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()) @@ -402,7 +385,7 @@ class ModuleDCCAllow : public Module { 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 @@ -426,7 +409,7 @@ class ModuleDCCAllow : public Module 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()) @@ -436,8 +419,8 @@ class ModuleDCCAllow : public Module 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; } @@ -466,7 +449,7 @@ class ModuleDCCAllow : public Module } } - void ReadFileConf() + void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE { bfl.clear(); ConfigTagList tags = ServerInstance->Config->ConfTags("banfile"); @@ -479,12 +462,7 @@ class ModuleDCCAllow : public Module } } - virtual ~ModuleDCCAllow() - { - delete ext; - } - - virtual Version GetVersion() + Version GetVersion() CXX11_OVERRIDE { return Version("Provides support for the /DCCALLOW command", VF_COMMON | VF_VENDOR); } diff --git a/src/modules/m_deaf.cpp b/src/modules/m_deaf.cpp index 43b24cfae..9800b32a9 100644 --- a/src/modules/m_deaf.cpp +++ b/src/modules/m_deaf.cpp @@ -21,8 +21,6 @@ #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 @@ -32,31 +30,20 @@ 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; @@ -66,84 +53,40 @@ class ModuleDeaf : public Module { } - 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) - { + Channel* chan = static_cast<Channel*>(dest); 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; - } + 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++) { /* 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 */ @@ -151,23 +94,20 @@ class ModuleDeaf : public Module 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) diff --git a/src/modules/m_delayjoin.cpp b/src/modules/m_delayjoin.cpp index a9a92e67a..dd07710bd 100644 --- a/src/modules/m_delayjoin.cpp +++ b/src/modules/m_delayjoin.cpp @@ -20,14 +20,11 @@ */ -/* $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) @@ -47,29 +44,21 @@ class ModuleDelayJoin : public Module { } - 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; + void 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) @@ -82,14 +71,10 @@ ModeAction DelayJoinMode::OnModeChange(User* source, User* dest, Channel* channe for (UserMembCIter n = names->begin(); n != names->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); @@ -119,7 +104,7 @@ static void populate(CUList& except, Membership* memb) 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); @@ -138,15 +123,15 @@ void ModuleDelayJoin::OnUserKick(User* source, Membership* memb, const std::stri 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; } } @@ -177,7 +162,7 @@ void ModuleDelayJoin::OnText(User* user, void* dest, int target_type, const std: } /* 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; diff --git a/src/modules/m_delaymsg.cpp b/src/modules/m_delaymsg.cpp index cfc06866a..1730663c5 100644 --- a/src/modules/m_delaymsg.cpp +++ b/src/modules/m_delaymsg.cpp @@ -19,13 +19,12 @@ #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; @@ -36,62 +35,49 @@ class DelayMsgMode : public ModeHandler 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; public: ModuleDelayMsg() : djm(this) { } - void init() - { - ServerInstance->Modules->AddService(djm); - ServerInstance->Modules->AddService(djm.jointime); - Implementation eventlist[] = { I_OnUserJoin, I_OnUserPreMessage}; - ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation)); - } - 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); + 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; }; -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 UserMembList* names = chan->GetUsers(); + for (UserMembCIter n = names->begin(); n != names->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); @@ -99,19 +85,19 @@ Version ModuleDelayMsg::GetVersion() 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) || (msgtype != MSG_PRIVMSG)) return MOD_RES_PASSTHRU; Channel* channel = (Channel*) dest; @@ -125,14 +111,14 @@ ModResult ModuleDelayMsg::OnUserPreMessage(User* user, void* dest, int target_ty 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; } } @@ -145,4 +131,3 @@ ModResult ModuleDelayMsg::OnUserPreMessage(User* user, void* dest, int target_ty } MODULE_INIT(ModuleDelayMsg) - diff --git a/src/modules/m_denychans.cpp b/src/modules/m_denychans.cpp index e774e92c8..184134025 100644 --- a/src/modules/m_denychans.cpp +++ b/src/modules/m_denychans.cpp @@ -22,18 +22,17 @@ #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"); @@ -45,10 +44,10 @@ class ModuleDenyChannels : public Module 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"); } @@ -67,8 +66,8 @@ class ModuleDenyChannels : public Module 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"); } } @@ -77,24 +76,20 @@ class ModuleDenyChannels : public Module } } - 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; } @@ -112,19 +107,19 @@ class ModuleDenyChannels : public Module } } - 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; } } diff --git a/src/modules/m_devoice.cpp b/src/modules/m_devoice.cpp index 2b5de2bd6..901f77b1a 100644 --- a/src/modules/m_devoice.cpp +++ b/src/modules/m_devoice.cpp @@ -24,8 +24,6 @@ * Syntax: /DEVOICE <#chan> */ -/* $ModDesc: Provides voiced users with the ability to devoice themselves. */ - #include "inspircd.h" /** Handle /DEVOICE @@ -36,24 +34,17 @@ class CommandDevoice : public Command 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->Modes->Process(modes, ServerInstance->FakeClient); + return CMD_SUCCESS; } }; @@ -65,16 +56,7 @@ class ModuleDeVoice : public Module { } - 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); } diff --git a/src/modules/m_dnsbl.cpp b/src/modules/m_dnsbl.cpp index d4101686a..63dda547f 100644 --- a/src/modules/m_dnsbl.cpp +++ b/src/modules/m_dnsbl.cpp @@ -23,8 +23,7 @@ #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 @@ -40,13 +39,12 @@ 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; @@ -55,170 +53,167 @@ class DNSBLResolver : public Resolver 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; @@ -241,25 +236,16 @@ class ModuleDNSBL : public Module 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(); @@ -291,7 +277,7 @@ class ModuleDNSBL : public Module } 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 */ @@ -316,11 +302,6 @@ class ModuleDNSBL : public Module 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()) @@ -336,14 +317,9 @@ class ModuleDNSBL : public Module } } - 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) @@ -352,40 +328,42 @@ class ModuleDNSBL : public Module 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)) @@ -396,15 +374,15 @@ class ModuleDNSBL : public Module 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; @@ -416,12 +394,12 @@ class ModuleDNSBL : public Module 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; } diff --git a/src/modules/m_exemptchanops.cpp b/src/modules/m_exemptchanops.cpp index 9fac8e7ad..14050430f 100644 --- a/src/modules/m_exemptchanops.cpp +++ b/src/modules/m_exemptchanops.cpp @@ -18,9 +18,7 @@ #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 */ @@ -31,30 +29,28 @@ class ExemptChanOps : public ListModeBase 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())) + if (!ServerInstance->Modes->FindMode(word, 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 :Mode doesn't exist", chan->name.c_str(), word.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()); } }; @@ -63,18 +59,14 @@ class ExemptHandler : public HandlerBase3<ModResult, User*, Channel*, const std: 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) @@ -82,11 +74,11 @@ class ExemptHandler : public HandlerBase3<ModResult, User*, Channel*, const std: 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) @@ -96,7 +88,7 @@ class ExemptHandler : public HandlerBase3<ModResult, User*, Channel*, const std: } } - ModeHandler* mh = FindMode(minmode); + PrefixMode* mh = FindMode(minmode); if (mh && mypfx >= mh->GetPrefixRank()) return MOD_RES_ALLOW; if (mh || minmode == "*") @@ -108,23 +100,16 @@ class ExemptHandler : public HandlerBase3<ModResult, User*, Channel*, const std: 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() @@ -132,20 +117,15 @@ class ModuleExemptChanOps : public Module 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) diff --git a/src/modules/m_filter.cpp b/src/modules/m_filter.cpp index 4090f5600..47041f824 100644 --- a/src/modules/m_filter.cpp +++ b/src/modules/m_filter.cpp @@ -22,11 +22,7 @@ #include "inspircd.h" #include "xline.h" -#include "m_regex.h" - -/* $ModDesc: Text (spam) filtering */ - -class ModuleFilter; +#include "modules/regex.h" enum FilterFlags { @@ -48,6 +44,7 @@ enum FilterAction class FilterResult { public: + Regex* regex; std::string freeform; std::string reason; FilterAction action; @@ -60,9 +57,12 @@ class FilterResult 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); } @@ -154,15 +154,6 @@ class CommandFilter : public Command } }; -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 { bool initing; @@ -173,28 +164,26 @@ class ModuleFilter : public Module 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. 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); @@ -209,13 +198,13 @@ CmdResult CommandFilter::Handle(const std::vector<std::string> ¶meters, User 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; } } @@ -232,7 +221,7 @@ CmdResult CommandFilter::Handle(const std::vector<std::string> ¶meters, User 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; } @@ -240,12 +229,12 @@ CmdResult CommandFilter::Handle(const std::vector<std::string> ¶meters, User { 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; } } @@ -258,9 +247,9 @@ CmdResult CommandFilter::Handle(const std::vector<std::string> ¶meters, User 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]); @@ -268,13 +257,13 @@ CmdResult CommandFilter::Handle(const std::vector<std::string> ¶meters, User } 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; } @@ -283,7 +272,7 @@ CmdResult CommandFilter::Handle(const std::vector<std::string> ¶meters, User 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; @@ -301,14 +290,6 @@ ModuleFilter::ModuleFilter() { } -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(); @@ -317,29 +298,19 @@ CullResult ModuleFilter::cull() 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) @@ -362,16 +333,16 @@ ModResult ModuleFilter::OnUserPreNotice(User* user,void* dest,int target_type, s { 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) { @@ -388,7 +359,7 @@ ModResult ModuleFilter::OnUserPreNotice(User* user,void* dest,int target_type, s 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; @@ -446,7 +417,7 @@ ModResult ModuleFilter::OnPreCommand(std::string &command, std::vector<std::stri /* 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) @@ -466,7 +437,7 @@ ModResult ModuleFilter::OnPreCommand(std::string &command, std::vector<std::stri return MOD_RES_PASSTHRU; } -void ModuleFilter::OnRehash(User* user) +void ModuleFilter::ReadConfig(ConfigStatus& status) { ConfigTagList tags = ServerInstance->Config->ConfTags("exemptfromfilter"); exemptfromfilter.clear(); @@ -554,11 +525,11 @@ FilterResult ModuleFilter::DecodeFilter(const std::string &data) 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))); } } @@ -573,27 +544,19 @@ void ModuleFilter::OnDecodeMetaData(Extensible* target, const std::string &extna } 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)) @@ -605,20 +568,15 @@ FilterResult* ModuleFilter::FilterMatch(User* user, const std::string &text, int 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) { @@ -632,7 +590,7 @@ bool ModuleFilter::DeleteFilter(const std::string &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) { @@ -642,11 +600,11 @@ std::pair<bool, std::string> ModuleFilter::AddFilter(const std::string &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, ""); @@ -695,7 +653,7 @@ void ModuleFilter::ReadFilters() 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 = "*"; @@ -705,12 +663,12 @@ void ModuleFilter::ReadFilters() 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()); } } } @@ -719,13 +677,13 @@ ModResult ModuleFilter::OnStats(char symbol, User* user, string_list &results) { 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(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+" :"+RegexEngine.GetProvider()+":"+i->freeform+" "+i->GetFlags()+" "+FilterActionToString(i->action)+" "+ConvToStr(i->gline_time)+" :"+i->reason); } for (std::set<std::string>::iterator i = exemptfromfilter.begin(); i != exemptfromfilter.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; diff --git a/src/modules/m_flashpolicyd.cpp b/src/modules/m_flashpolicyd.cpp new file mode 100644 index 000000000..95b82848f --- /dev/null +++ b/src/modules/m_flashpolicyd.cpp @@ -0,0 +1,158 @@ +/* + * 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(','); + } + 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) diff --git a/src/modules/m_gecosban.cpp b/src/modules/m_gecosban.cpp index 1497c1b87..a15f19418 100644 --- a/src/modules/m_gecosban.cpp +++ b/src/modules/m_gecosban.cpp @@ -19,27 +19,15 @@ #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] == ':')) { @@ -49,9 +37,9 @@ class ModuleGecosBan : public Module 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'); } }; diff --git a/src/modules/m_globalload.cpp b/src/modules/m_globalload.cpp index 9e0ec7705..8ee8472e6 100644 --- a/src/modules/m_globalload.cpp +++ b/src/modules/m_globalload.cpp @@ -22,8 +22,6 @@ */ -/* $ModDesc: Allows global loading of a module. */ - #include "inspircd.h" /** Handle /GLOADMODULE @@ -35,7 +33,6 @@ class CommandGloadmodule : public Command { flags_needed = 'o'; syntax = "<modulename> [servermask]"; - TRANSLATE3(TR_TEXT, TR_TEXT, TR_END); } CmdResult Handle (const std::vector<std::string> ¶meters, User *user) @@ -47,11 +44,11 @@ class CommandGloadmodule : public Command 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 @@ -79,6 +76,13 @@ class CommandGunloadmodule : public Command 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)) @@ -94,11 +98,11 @@ class CommandGunloadmodule : public Command } 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()); @@ -125,8 +129,8 @@ class GReloadModuleWorker : public HandlerBase1<void, bool> 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); } }; @@ -157,7 +161,7 @@ class CommandGreloadmodule : public Command } 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; } } @@ -185,22 +189,10 @@ class ModuleGlobalLoad : public Module { } - 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) - diff --git a/src/modules/m_globops.cpp b/src/modules/m_globops.cpp index 85d84252b..1cb87324b 100644 --- a/src/modules/m_globops.cpp +++ b/src/modules/m_globops.cpp @@ -23,8 +23,6 @@ #include "inspircd.h" -/* $ModDesc: Provides support for GLOBOPS and snomask +g */ - /** Handle /GLOBOPS */ class CommandGlobops : public Command @@ -33,7 +31,6 @@ 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) @@ -49,17 +46,15 @@ class ModuleGlobops : public Module 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) diff --git a/src/modules/m_halfop.cpp b/src/modules/m_halfop.cpp deleted file mode 100644 index 3194fcde8..000000000 --- a/src/modules/m_halfop.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/* - * 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) diff --git a/src/modules/m_helpop.cpp b/src/modules/m_helpop.cpp index 4bbe8785e..2fe958a71 100644 --- a/src/modules/m_helpop.cpp +++ b/src/modules/m_helpop.cpp @@ -21,11 +21,10 @@ */ -/* $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 */ @@ -42,41 +41,40 @@ class Helpop : public SimpleUserModeHandler */ 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 = "*"; @@ -84,13 +82,13 @@ class CommandHelpop : public Command { // 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; } @@ -98,7 +96,6 @@ class CommandHelpop : public Command class ModuleHelpop : public Module { - private: CommandHelpop cmd; Helpop ho; @@ -108,24 +105,15 @@ class ModuleHelpop : public Module { } - 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 */ @@ -151,20 +139,15 @@ class ModuleHelpop : public Module 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); } diff --git a/src/modules/m_hidechans.cpp b/src/modules/m_hidechans.cpp index 008c62208..cd3ac2c26 100644 --- a/src/modules/m_hidechans.cpp +++ b/src/modules/m_hidechans.cpp @@ -20,8 +20,6 @@ #include "inspircd.h" -/* $ModDesc: Provides support for hiding channels with user mode +I */ - /** Handles user mode +I */ class HideChans : public SimpleUserModeHandler @@ -39,29 +37,17 @@ class ModuleHideChans : public Module { } - 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) @@ -72,7 +58,7 @@ class ModuleHideChans : public Module 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 */ @@ -88,5 +74,4 @@ class ModuleHideChans : public Module } }; - MODULE_INIT(ModuleHideChans) diff --git a/src/modules/m_hideoper.cpp b/src/modules/m_hideoper.cpp index fbab9685f..d3c2bf444 100644 --- a/src/modules/m_hideoper.cpp +++ b/src/modules/m_hideoper.cpp @@ -21,8 +21,6 @@ #include "inspircd.h" -/* $ModDesc: Provides support for hiding oper status with user mode +H */ - /** Handles user mode +H */ class HideOper : public SimpleUserModeHandler @@ -43,24 +41,12 @@ class ModuleHideOper : public Module { } - void init() - { - ServerInstance->Modules->AddService(hm); - Implementation eventlist[] = { I_OnWhoisLine, I_OnSendWhoLine }; - 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 @@ -68,7 +54,7 @@ class ModuleHideOper : public Module if (numeric != 313) return MOD_RES_PASSTHRU; - if (!dest->IsModeSet('H')) + if (!dest->IsModeSet(hm)) return MOD_RES_PASSTHRU; if (!user->HasPrivPermission("users/auspex")) @@ -77,9 +63,9 @@ class ModuleHideOper : public Module 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("*"); @@ -92,5 +78,4 @@ class ModuleHideOper : public Module } }; - MODULE_INIT(ModuleHideOper) diff --git a/src/modules/m_hostchange.cpp b/src/modules/m_hostchange.cpp index 7433fccd3..6d5896ef5 100644 --- a/src/modules/m_hostchange.cpp +++ b/src/modules/m_hostchange.cpp @@ -21,8 +21,6 @@ #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 @@ -47,21 +45,13 @@ typedef std::vector<std::pair<std::string, Host> > hostchanges_t; 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"); @@ -97,14 +87,14 @@ class ModuleHostChange : public Module } } - 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++) { @@ -160,9 +150,9 @@ class ModuleHostChange : public Module } 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; } } diff --git a/src/modules/m_hostcycle.cpp b/src/modules/m_hostcycle.cpp new file mode 100644 index 000000000..d3646e899 --- /dev/null +++ b/src/modules/m_hostcycle.cpp @@ -0,0 +1,114 @@ +/* + * 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 UserMembList* ulist = c->GetUsers(); + for (UserMembList::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) diff --git a/src/modules/m_httpd.cpp b/src/modules/m_httpd.cpp index 2a430d967..053f4b924 100644 --- a/src/modules/m_httpd.cpp +++ b/src/modules/m_httpd.cpp @@ -23,10 +23,8 @@ #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; @@ -67,9 +65,8 @@ class HttpServerSocket : public BufferedSocket { 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() @@ -77,7 +74,7 @@ class HttpServerSocket : public BufferedSocket sockets.erase(this); } - virtual void OnError(BufferedSocketError) + void OnError(BufferedSocketError) CXX11_OVERRIDE { ServerInstance->GlobalCulls.AddItem(this); } @@ -225,7 +222,7 @@ class HttpServerSocket : public BufferedSocket 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"); } @@ -339,36 +336,45 @@ class HttpServerSocket : public BufferedSocket } }; +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; @@ -379,7 +385,7 @@ class ModuleHttpServer : public Module return MOD_RES_ALLOW; } - void OnBackgroundTimer(time_t curtime) + void OnBackgroundTimer(time_t curtime) CXX11_OVERRIDE { if (!timeoutsec) return; @@ -397,7 +403,7 @@ class ModuleHttpServer : public Module } } - CullResult cull() + CullResult cull() CXX11_OVERRIDE { std::set<HttpServerSocket*> local; local.swap(sockets); @@ -410,7 +416,7 @@ class ModuleHttpServer : public Module return Module::cull(); } - virtual Version GetVersion() + Version GetVersion() CXX11_OVERRIDE { return Version("Provides HTTP serving facilities to modules", VF_VENDOR); } diff --git a/src/modules/m_httpd_acl.cpp b/src/modules/m_httpd_acl.cpp index c25cabc0a..74d25deba 100644 --- a/src/modules/m_httpd_acl.cpp +++ b/src/modules/m_httpd_acl.cpp @@ -19,11 +19,9 @@ #include "inspircd.h" -#include "httpd.h" +#include "modules/httpd.h" #include "protocol.h" -/* $ModDesc: Provides access control lists (passwording of resources, ip restrictions etc) to m_httpd.so dependent modules */ - class HTTPACL { public: @@ -37,19 +35,21 @@ 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"); @@ -86,37 +86,30 @@ class ModuleHTTPAccessList : public Module } } - 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) @@ -133,7 +126,7 @@ class ModuleHTTPAccessList : public Module { 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; @@ -155,7 +148,7 @@ class ModuleHTTPAccessList : public Module 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; @@ -164,7 +157,7 @@ class ModuleHTTPAccessList : public Module 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")) @@ -183,7 +176,7 @@ class ModuleHTTPAccessList : public Module 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)) @@ -193,7 +186,7 @@ class ModuleHTTPAccessList : public Module /* 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 @@ -222,11 +215,7 @@ class ModuleHTTPAccessList : public Module } } - 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); } diff --git a/src/modules/m_httpd_config.cpp b/src/modules/m_httpd_config.cpp index 62314cd7e..8333d9f9c 100644 --- a/src/modules/m_httpd_config.cpp +++ b/src/modules/m_httpd_config.cpp @@ -19,18 +19,17 @@ #include "inspircd.h" -#include "httpd.h" +#include "modules/httpd.h" #include "protocol.h" -/* $ModDesc: Allows for the server configuration to be viewed over HTTP via m_httpd.so */ - 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) @@ -67,13 +66,13 @@ class ModuleHttpConfig : public Module 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/")) @@ -95,18 +94,14 @@ class ModuleHttpConfig : public Module 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); } diff --git a/src/modules/m_httpd_stats.cpp b/src/modules/m_httpd_stats.cpp index 2fc7ca7de..2dcf1e1cf 100644 --- a/src/modules/m_httpd_stats.cpp +++ b/src/modules/m_httpd_stats.cpp @@ -22,22 +22,19 @@ #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) @@ -91,13 +88,13 @@ class ModuleHttpStats : public Module 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/")) @@ -107,10 +104,10 @@ class ModuleHttpStats : public Module << 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(); @@ -119,7 +116,13 @@ class ModuleHttpStats : public Module 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) { @@ -138,19 +141,19 @@ class ModuleHttpStats : public Module } 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>"; @@ -166,7 +169,7 @@ class ModuleHttpStats : public Module { 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>"; @@ -179,23 +182,24 @@ class ModuleHttpStats : public Module 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); @@ -205,10 +209,10 @@ class ModuleHttpStats : public Module 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>"; @@ -225,18 +229,14 @@ class ModuleHttpStats : public Module /* 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); } diff --git a/src/modules/m_ident.cpp b/src/modules/m_ident.cpp index f0ced1db7..57944737c 100644 --- a/src/modules/m_ident.cpp +++ b/src/modules/m_ident.cpp @@ -24,8 +24,6 @@ #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 @@ -119,33 +117,33 @@ class IdentRequestSocket : public EventHandler } /* 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]; @@ -161,11 +159,11 @@ class IdentRequestSocket : public EventHandler /* 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) { @@ -179,7 +177,7 @@ class IdentRequestSocket : public EventHandler 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! */ @@ -196,10 +194,8 @@ class IdentRequestSocket : public EventHandler */ 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); } } @@ -213,8 +209,8 @@ class IdentRequestSocket : public EventHandler /* 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 @@ -228,7 +224,7 @@ class IdentRequestSocket : public EventHandler 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). @@ -260,7 +256,7 @@ class IdentRequestSocket : public EventHandler * we're done. */ result += *i; - if (!ServerInstance->IsIdent(result.c_str())) + if (!ServerInstance->IsIdent(result)) { result.erase(result.end()-1); break; @@ -272,46 +268,31 @@ class IdentRequestSocket : public EventHandler class ModuleIdent : public Module { int RequestTimeout; - SimpleExtItem<IdentRequestSocket> ext; + 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; } - 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 { @@ -320,7 +301,7 @@ class ModuleIdent : public Module } catch (ModuleException &e) { - ServerInstance->Logs->Log("m_ident",DEBUG,"Ident exception: %s", e.GetReason()); + ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Ident exception: " + e.GetReason()); } } @@ -328,17 +309,12 @@ class ModuleIdent : public Module * 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 :("); return MOD_RES_PASSTHRU; - } - - ServerInstance->Logs->Log("m_ident",DEBUG, "Has ident_socket"); time_t compare = isock->age; compare += RequestTimeout; @@ -347,28 +323,24 @@ class ModuleIdent : public Module 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(); @@ -377,14 +349,14 @@ class ModuleIdent : public Module 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) + void OnCleanup(int target_type, void *item) CXX11_OVERRIDE { /* Module unloading, tidy up users */ if (target_type == TYPE_USER) @@ -395,7 +367,7 @@ class ModuleIdent : public Module } } - virtual void OnUserDisconnect(LocalUser *user) + void OnUserDisconnect(LocalUser *user) CXX11_OVERRIDE { /* User disconnect (generic socket detatch event) */ IdentRequestSocket *isock = ext.get(user); @@ -408,4 +380,3 @@ class ModuleIdent : public Module }; MODULE_INIT(ModuleIdent) - diff --git a/src/modules/m_inviteexception.cpp b/src/modules/m_inviteexception.cpp index 747a3b30a..6229e1fa2 100644 --- a/src/modules/m_inviteexception.cpp +++ b/src/modules/m_inviteexception.cpp @@ -22,10 +22,7 @@ #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. @@ -54,27 +51,17 @@ public: { } - 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)) { @@ -86,25 +73,20 @@ public: 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); } diff --git a/src/modules/m_ircv3.cpp b/src/modules/m_ircv3.cpp index da42d823d..5cb2ab6b1 100644 --- a/src/modules/m_ircv3.cpp +++ b/src/modules/m_ircv3.cpp @@ -16,11 +16,9 @@ * 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 { @@ -35,10 +33,10 @@ 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) @@ -50,9 +48,9 @@ class ModuleIRCv3 : public Module // 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(); + const UserMembList* userlist = (*i)->chan->GetUsers(); for (UserMembList::const_iterator m = userlist->begin(); m != userlist->end(); ++m) { /* @@ -78,22 +76,15 @@ class ModuleIRCv3 : public Module { } - 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); @@ -122,10 +113,10 @@ class ModuleIRCv3 : public Module } } - 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) @@ -195,7 +186,7 @@ class ModuleIRCv3 : public Module } } - ModResult OnSetAway(User* user, const std::string &awaymsg) + ModResult OnSetAway(User* user, const std::string &awaymsg) CXX11_OVERRIDE { if (awaynotify) { @@ -210,9 +201,9 @@ class ModuleIRCv3 : public Module 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; @@ -236,7 +227,7 @@ class ModuleIRCv3 : public Module 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); } diff --git a/src/modules/m_joinflood.cpp b/src/modules/m_joinflood.cpp index 63bcc38a4..52802f168 100644 --- a/src/modules/m_joinflood.cpp +++ b/src/modules/m_joinflood.cpp @@ -23,35 +23,32 @@ #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() @@ -66,155 +63,87 @@ class joinfloodsettings 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) @@ -235,11 +164,7 @@ class ModuleJoinFlood : public Module } } - ~ModuleJoinFlood() - { - } - - Version GetVersion() + Version GetVersion() CXX11_OVERRIDE { return Version("Provides channel mode +j (join flood protection)", VF_VENDOR); } diff --git a/src/modules/m_jumpserver.cpp b/src/modules/m_jumpserver.cpp index dce8f0bd5..523500e50 100644 --- a/src/modules/m_jumpserver.cpp +++ b/src/modules/m_jumpserver.cpp @@ -20,8 +20,7 @@ #include "inspircd.h" - -/* $ModDesc: Provides support for the RPL_REDIR numeric and the /JUMPSERVER command. */ +#include "modules/ssl.h" /** Handle /JUMPSERVER */ @@ -32,11 +31,14 @@ class CommandJumpserver : public Command 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; } @@ -53,11 +55,12 @@ class CommandJumpserver : public Command 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; } @@ -84,15 +87,21 @@ class CommandJumpserver : public Command 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; } @@ -101,10 +110,10 @@ class CommandJumpserver : public Command /* 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) { - 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++; } @@ -116,24 +125,24 @@ class CommandJumpserver : public Command } 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 { @@ -143,40 +152,30 @@ 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) diff --git a/src/modules/m_kicknorejoin.cpp b/src/modules/m_kicknorejoin.cpp index a914f3869..fdb7b8f24 100644 --- a/src/modules/m_kicknorejoin.cpp +++ b/src/modules/m_kicknorejoin.cpp @@ -25,49 +25,44 @@ #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 @@ -75,44 +70,27 @@ 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; @@ -120,40 +98,30 @@ public: 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) diff --git a/src/modules/m_knock.cpp b/src/modules/m_knock.cpp index 8d2aa4543..26397bc9c 100644 --- a/src/modules/m_knock.cpp +++ b/src/modules/m_knock.cpp @@ -21,20 +21,23 @@ #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) @@ -42,25 +45,25 @@ class CommandKnock : public Command 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; } @@ -70,7 +73,7 @@ class CommandKnock : public Command 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; } @@ -80,33 +83,19 @@ class CommandKnock : public Command } }; -/** 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()); @@ -128,7 +117,7 @@ class ModuleKnock : public Module } } - virtual Version GetVersion() + Version GetVersion() CXX11_OVERRIDE { return Version("Provides support for /KNOCK and channel mode +K", VF_OPTCOMMON | VF_VENDOR); } diff --git a/src/modules/m_ldapauth.cpp b/src/modules/m_ldapauth.cpp new file mode 100644 index 000000000..e89ce4949 --- /dev/null +++ b/src/modules/m_ldapauth.cpp @@ -0,0 +1,434 @@ +/* + * 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) diff --git a/src/modules/m_ldapoper.cpp b/src/modules/m_ldapoper.cpp new file mode 100644 index 000000000..9bfa3971f --- /dev/null +++ b/src/modules/m_ldapoper.cpp @@ -0,0 +1,248 @@ +/* + * 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); + OperIndex::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]; + + OperIndex::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) diff --git a/src/modules/m_lockserv.cpp b/src/modules/m_lockserv.cpp index 4983ae16a..65b9aa036 100644 --- a/src/modules/m_lockserv.cpp +++ b/src/modules/m_lockserv.cpp @@ -20,18 +20,16 @@ #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'; @@ -41,12 +39,12 @@ public: { 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; } @@ -54,10 +52,9 @@ public: class CommandUnlockserv : public Command { -private: bool& locked; -public: + public: CommandUnlockserv(Module* Creator, bool &lock) : Command(Creator, "UNLOCKSERV", 0), locked(lock) { flags_needed = 'o'; @@ -67,12 +64,12 @@ public: { 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; } @@ -80,37 +77,28 @@ public: 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) { @@ -120,12 +108,12 @@ public: 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); } diff --git a/src/modules/m_maphide.cpp b/src/modules/m_maphide.cpp index 546e342ae..41de2997d 100644 --- a/src/modules/m_maphide.cpp +++ b/src/modules/m_maphide.cpp @@ -19,44 +19,30 @@ #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) - diff --git a/src/modules/m_md5.cpp b/src/modules/m_md5.cpp index 14ccf16a8..ecf76d07c 100644 --- a/src/modules/m_md5.cpp +++ b/src/modules/m_md5.cpp @@ -21,13 +21,8 @@ */ -/* $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))) @@ -39,10 +34,6 @@ #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; @@ -275,13 +266,6 @@ class MD5Provider : public HashProvider 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) {} }; @@ -291,10 +275,9 @@ 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); } diff --git a/src/modules/m_messageflood.cpp b/src/modules/m_messageflood.cpp index 9ff17924d..92d67b9ab 100644 --- a/src/modules/m_messageflood.cpp +++ b/src/modules/m_messageflood.cpp @@ -25,8 +25,6 @@ #include "inspircd.h" -/* $ModDesc: Provides channel mode +f (message flood protection) */ - /** Holds flood settings and state for mode +f */ class floodsettings @@ -66,54 +64,44 @@ 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)); } }; @@ -128,17 +116,13 @@ class ModuleMsgFlood : public Module { } - 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) @@ -156,14 +140,14 @@ class ModuleMsgFlood : public Module std::vector<std::string> parameters; parameters.push_back(dest->name); parameters.push_back("+b"); - parameters.push_back(user->MakeWildHost()); - ServerInstance->SendGlobalMode(parameters, ServerInstance->FakeClient); + parameters.push_back("*!*@" + user->dhost); + ServerInstance->Modes->Process(parameters, ServerInstance->FakeClient); } - 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; } @@ -172,30 +156,13 @@ class ModuleMsgFlood : public Module 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); } diff --git a/src/modules/m_mlock.cpp b/src/modules/m_mlock.cpp index d1df81354..d9c43ec10 100644 --- a/src/modules/m_mlock.cpp +++ b/src/modules/m_mlock.cpp @@ -17,30 +17,24 @@ */ -/* $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; @@ -52,6 +46,7 @@ public: 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) { @@ -62,7 +57,6 @@ public: return MOD_RES_PASSTHRU; } - }; MODULE_INIT(ModuleMLock) diff --git a/src/commands/cmd_modenotice.cpp b/src/modules/m_modenotice.cpp index 852b72f6a..e02c9147f 100644 --- a/src/commands/cmd_modenotice.cpp +++ b/src/modules/m_modenotice.cpp @@ -30,6 +30,7 @@ class CommandModeNotice : public Command CmdResult Handle(const std::vector<std::string>& parameters, User *src) { + std::string msg = "*** From " + src->nick + ": " + parameters[1]; int mlen = parameters[0].length(); for (LocalUserList::const_iterator i = ServerInstance->Users->local_users.begin(); i != ServerInstance->Users->local_users.end(); i++) { @@ -39,8 +40,7 @@ class CommandModeNotice : public Command 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()); + user->WriteNotice(msg); next_user: ; } return CMD_SUCCESS; @@ -48,8 +48,24 @@ next_user: ; RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { - return ROUTE_BROADCAST; + return ROUTE_OPT_BCAST; } }; -COMMAND_INIT(CommandModeNotice) +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) diff --git a/src/modules/m_muteban.cpp b/src/modules/m_muteban.cpp index 767af2901..72c4acd47 100644 --- a/src/modules/m_muteban.cpp +++ b/src/modules/m_muteban.cpp @@ -20,28 +20,15 @@ #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; @@ -49,24 +36,17 @@ class ModuleQuietBan : public Module 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) - diff --git a/src/modules/m_namedmodes.cpp b/src/modules/m_namedmodes.cpp index 4db1f70b9..5c0ffeea5 100644 --- a/src/modules/m_namedmodes.cpp +++ b/src/modules/m_namedmodes.cpp @@ -17,28 +17,24 @@ */ -/* $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 @@ -69,21 +65,19 @@ class CommandProp : public Command 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) + modes[1].push_back(plus ? '+' : '-'); + modes[1].push_back(mh->GetModeChar()); + 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_back(parameters[i++]); } } } - ServerInstance->SendGlobalMode(modes, src); + ServerInstance->Modes->Process(modes, src); return CMD_SUCCESS; } }; @@ -106,16 +100,7 @@ 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); } @@ -125,7 +110,7 @@ class ModuleNamedModes : public Module 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, const std::vector<std::string>& parameters) CXX11_OVERRIDE { if (!channel) return MOD_RES_PASSTHRU; @@ -155,7 +140,6 @@ class ModuleNamedModes : public Module ModeHandler *mh = ServerInstance->Modes->FindMode(modechar, MODETYPE_CHANNEL); if (modechar == 'Z') { - modechar = 0; std::string name, value; if (param_at < parameters.size()) name = parameters[param_at++]; @@ -165,31 +149,28 @@ class ModuleNamedModes : public Module value = name.substr(eq + 1); name = name.substr(0, eq); } - for(char letter = 'A'; modechar == 0 && letter <= 'z'; letter++) + + mh = ServerInstance->Modes->FindMode(name, MODETYPE_CHANNEL); + if (!mh) + { + // Mode handler not found + modelist.erase(i--, 1); + continue; + } + + if (mh->GetNumParams(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 + modelist.erase(i--, 1); + continue; } + + newparms.push_back(value); } - if (modechar) - modelist[i] = modechar; - else - modelist.erase(i--, 1); + + modelist[i] = mh->GetModeChar(); } else if (mh && mh->GetNumParams(adding) && param_at < parameters.size()) { @@ -197,7 +178,7 @@ class ModuleNamedModes : public Module } } newparms[1] = modelist; - ServerInstance->Modes->Process(newparms, source, false); + ServerInstance->Modes->Process(newparms, source); return MOD_RES_DENY; } }; diff --git a/src/modules/m_namesx.cpp b/src/modules/m_namesx.cpp index 82d311773..4dc5ff468 100644 --- a/src/modules/m_namesx.cpp +++ b/src/modules/m_namesx.cpp @@ -21,40 +21,27 @@ #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. @@ -72,7 +59,7 @@ class ModuleNamesX : public Module return MOD_RES_PASSTHRU; } - void OnNamesListItem(User* issuer, Membership* memb, std::string &prefixes, std::string &nick) + void OnNamesListItem(User* issuer, Membership* memb, std::string &prefixes, std::string &nick) CXX11_OVERRIDE { if (!cap.ext.get(issuer)) return; @@ -81,12 +68,12 @@ class ModuleNamesX : public Module if (nick.empty()) return; - prefixes = memb->chan->GetAllPrefixChars(memb->user); + prefixes = memb->GetAllPrefixChars(); } - 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 @@ -101,20 +88,10 @@ class ModuleNamesX : public Module 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; @@ -122,7 +99,7 @@ class ModuleNamesX : public Module line.insert(pos, prefixes); } - void OnEvent(Event& ev) + void OnEvent(Event& ev) CXX11_OVERRIDE { cap.HandleEvent(ev); } diff --git a/src/modules/m_nationalchars.cpp b/src/modules/m_nationalchars.cpp index b43b6e2b6..eb2d080c8 100644 --- a/src/modules/m_nationalchars.cpp +++ b/src/modules/m_nationalchars.cpp @@ -29,14 +29,10 @@ #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];*/ @@ -71,11 +67,12 @@ char utf8size(unsigned char * mb) /* 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++) { @@ -215,17 +212,16 @@ bool lwbNickHandler::Call(const char* n, size_t max) } /* 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; @@ -235,26 +231,20 @@ class ModuleNationalChars : public Module { } - 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"); @@ -276,19 +266,19 @@ class ModuleNationalChars : public Module { /* 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; CheckForceQuit("National characters module unloaded"); } - 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); } @@ -304,10 +294,10 @@ class ModuleNationalChars : public Module /*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; } @@ -322,7 +312,7 @@ class ModuleNationalChars : public Module { 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; } } diff --git a/src/modules/m_nickflood.cpp b/src/modules/m_nickflood.cpp index 04d7c8b5e..f74a18422 100644 --- a/src/modules/m_nickflood.cpp +++ b/src/modules/m_nickflood.cpp @@ -20,8 +20,6 @@ #include "inspircd.h" -/* $ModDesc: Provides channel mode +F (nick flood protection) */ - /** Holds settings and state associated with channel mode +F */ class nickfloodsettings @@ -80,53 +78,41 @@ 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)); } }; @@ -135,28 +121,16 @@ class ModuleNickFlood : public Module 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(User* 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++) { - Channel *channel = *i; + Channel* channel = (*i)->chan; ModResult res; nickfloodsettings *f = nf.ext.get(channel); @@ -168,7 +142,7 @@ class ModuleNickFlood : public Module 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; } @@ -188,14 +162,14 @@ class ModuleNickFlood : public Module /* * 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) { - Channel *channel = *i; + Channel* channel = (*i)->chan; ModResult res; nickfloodsettings *f = nf.ext.get(channel); @@ -214,11 +188,7 @@ class ModuleNickFlood : public Module } } - ~ModuleNickFlood() - { - } - - Version GetVersion() + Version GetVersion() CXX11_OVERRIDE { return Version("Channel mode F - nick flood protection", VF_VENDOR); } diff --git a/src/modules/m_nicklock.cpp b/src/modules/m_nicklock.cpp index 4f5e5941c..b8d4ac4df 100644 --- a/src/modules/m_nicklock.cpp +++ b/src/modules/m_nicklock.cpp @@ -22,8 +22,6 @@ #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 @@ -35,7 +33,7 @@ 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) @@ -44,20 +42,20 @@ class CommandNicklock : public Command 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 */ @@ -66,7 +64,7 @@ class CommandNicklock : public Command locked.set(target, 1); std::string oldnick = target->nick; - if (target->ForceNickChange(parameters[1].c_str())) + if (target->ForceNickChange(parameters[1])) ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used NICKLOCK to change and hold "+oldnick+" to "+parameters[1]); else { @@ -98,7 +96,7 @@ class CommandNickunlock : public Command { flags_needed = 'o'; syntax = "<locked-nick>"; - TRANSLATE2(TR_NICK, TR_END); + TRANSLATE1(TR_NICK); } CmdResult Handle (const std::vector<std::string>& parameters, User *user) @@ -107,7 +105,7 @@ class CommandNickunlock : public Command 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; } @@ -139,7 +137,6 @@ class CommandNickunlock : public Command } }; - class ModuleNickLock : public Module { LocalIntExt locked; @@ -151,34 +148,19 @@ class ModuleNickLock : public Module { } - 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(User* 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; diff --git a/src/modules/m_noctcp.cpp b/src/modules/m_noctcp.cpp index 1dd6fe34a..953557d90 100644 --- a/src/modules/m_noctcp.cpp +++ b/src/modules/m_noctcp.cpp @@ -21,8 +21,6 @@ #include "inspircd.h" -/* $ModDesc: Provides channel mode +C to block CTCPs */ - class NoCTCP : public SimpleChannelModeHandler { public: @@ -31,38 +29,20 @@ class NoCTCP : public SimpleChannelModeHandler 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))) { @@ -74,18 +54,18 @@ class ModuleNoCTCP : public Module 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'); } }; diff --git a/src/modules/m_nokicks.cpp b/src/modules/m_nokicks.cpp index 1f58a2e08..0acf84118 100644 --- a/src/modules/m_nokicks.cpp +++ b/src/modules/m_nokicks.cpp @@ -22,8 +22,6 @@ #include "inspircd.h" -/* $ModDesc: Provides channel mode +Q to prevent kicks on the channel. */ - class NoKicks : public SimpleChannelModeHandler { public: @@ -40,38 +38,26 @@ class ModuleNoKicks : public Module { } - 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) diff --git a/src/modules/m_nonicks.cpp b/src/modules/m_nonicks.cpp index 672a48f8d..15ee4e7f8 100644 --- a/src/modules/m_nonicks.cpp +++ b/src/modules/m_nonicks.cpp @@ -21,8 +21,6 @@ #include "inspircd.h" -/* $ModDesc: Provides support for channel mode +N & extban +b N: which prevents nick changes on channel */ - class NoNicks : public SimpleChannelModeHandler { public: @@ -38,54 +36,37 @@ class ModuleNoNickChange : public Module { } - 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(User* 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++) { - 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; } } @@ -93,7 +74,7 @@ class ModuleNoNickChange : public Module return MOD_RES_PASSTHRU; } - virtual void OnRehash(User* user) + void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE { override = ServerInstance->Config->ConfValue("nonicks")->getBool("operoverride", false); } diff --git a/src/modules/m_nonotice.cpp b/src/modules/m_nonotice.cpp index c5b9f3a1c..cab367ad9 100644 --- a/src/modules/m_nonotice.cpp +++ b/src/modules/m_nonotice.cpp @@ -21,8 +21,6 @@ #include "inspircd.h" -/* $ModDesc: Provides channel mode +T to block notices to the channel */ - class NoNotice : public SimpleChannelModeHandler { public: @@ -39,32 +37,25 @@ class ModuleNoNotice : public Module { } - 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; } } @@ -72,11 +63,7 @@ class ModuleNoNotice : public Module 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); } diff --git a/src/modules/m_nopartmsg.cpp b/src/modules/m_nopartmsg.cpp index ad3413101..7aeb66920 100644 --- a/src/modules/m_nopartmsg.cpp +++ b/src/modules/m_nopartmsg.cpp @@ -19,45 +19,27 @@ #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) - diff --git a/src/modules/m_ojoin.cpp b/src/modules/m_ojoin.cpp index 8f8d3ca90..e4314873b 100644 --- a/src/modules/m_ojoin.cpp +++ b/src/modules/m_ojoin.cpp @@ -1,6 +1,7 @@ /* * 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 @@ -16,57 +17,38 @@ * 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) : + SplitCommand(parent, "OJOIN", 1) { 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) @@ -87,11 +69,14 @@ class CommandOjoin : public Command // 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.push_back(std::string("+") + npmh->GetModeChar()); if (op) + { + modes[1].push_back('o'); modes.push_back(user->nick); - ServerInstance->SendGlobalMode(modes, ServerInstance->FakeClient); + } + modes.push_back(user->nick); + ServerInstance->Modes->Process(modes, ServerInstance->FakeClient); } return CMD_SUCCESS; } @@ -99,54 +84,15 @@ class CommandOjoin : public Command /** 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') { - 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) - { + prefixrank = NETWORK_VALUE; } ModResult AccessCheck(User* source, Channel* channel, std::string ¶meter, bool adding) @@ -158,12 +104,6 @@ class NetworkPrefix : public ModeHandler return MOD_RES_PASSTHRU; } - - ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding) - { - return MODEACTION_ALLOW; - } - }; class ModuleOjoin : public Module @@ -178,27 +118,26 @@ class ModuleOjoin : public Module { } - void init() + void init() CXX11_OVERRIDE { - /* Load config stuff */ - OnRehash(NULL); + std::string npre = ServerInstance->Config->ConfValue("ojoin")->getString("prefix"); + char NPrefix = npre.empty() ? 0 : npre[0]; + if (NPrefix && ServerInstance->Modes->FindPrefix(NPrefix)) + throw ModuleException("Looks like the prefix you picked for m_ojoin is already in use. Pick another."); /* Initialise module variables */ - np = new NetworkPrefix(this); + np = new NetworkPrefix(this, NPrefix); + mycommand.npmh = np; 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; } @@ -206,35 +145,24 @@ class ModuleOjoin : public Module 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; } @@ -248,11 +176,10 @@ class ModuleOjoin : public Module ServerInstance->Modules->SetPriority(this, I_OnUserPreJoin, PRIORITY_FIRST); } - Version GetVersion() + Version GetVersion() CXX11_OVERRIDE { return Version("Network Business Join", VF_VENDOR); } }; MODULE_INIT(ModuleOjoin) - diff --git a/src/modules/m_operchans.cpp b/src/modules/m_operchans.cpp index ca948d95b..3c6b4cd59 100644 --- a/src/modules/m_operchans.cpp +++ b/src/modules/m_operchans.cpp @@ -22,8 +22,6 @@ #include "inspircd.h" -/* $ModDesc: Provides support for oper-only chans via the +O channel mode */ - class OperChans : public SimpleChannelModeHandler { public: @@ -42,44 +40,33 @@ class ModuleOperChans : public Module { } - 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); } diff --git a/src/modules/m_operjoin.cpp b/src/modules/m_operjoin.cpp index bd77384a6..dd80d99ba 100644 --- a/src/modules/m_operjoin.cpp +++ b/src/modules/m_operjoin.cpp @@ -24,84 +24,44 @@ #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); } } }; diff --git a/src/modules/m_operlevels.cpp b/src/modules/m_operlevels.cpp index 569defd49..ac7178a93 100644 --- a/src/modules/m_operlevels.cpp +++ b/src/modules/m_operlevels.cpp @@ -20,27 +20,20 @@ */ -/* $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()); @@ -50,8 +43,8 @@ class ModuleOperLevels : public Module 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; } } @@ -60,4 +53,3 @@ class ModuleOperLevels : public Module }; MODULE_INIT(ModuleOperLevels) - diff --git a/src/modules/m_operlog.cpp b/src/modules/m_operlog.cpp index edb9109e8..e89dda7b5 100644 --- a/src/modules/m_operlog.cpp +++ b/src/modules/m_operlog.cpp @@ -21,51 +21,39 @@ #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()) && (IS_LOCAL(user)) && (user->HasPermission(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); } @@ -74,12 +62,11 @@ class ModuleOperLog : public Module 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) diff --git a/src/modules/m_opermodes.cpp b/src/modules/m_opermodes.cpp index 8b49f685e..7ab54cedf 100644 --- a/src/modules/m_opermodes.cpp +++ b/src/modules/m_opermodes.cpp @@ -22,26 +22,15 @@ #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; @@ -71,7 +60,7 @@ class ModuleModesOnOper : public Module while (ss >> buf) modes.push_back(buf); - ServerInstance->SendMode(modes, u); + ServerInstance->Modes->Process(modes, u); } }; diff --git a/src/modules/m_opermotd.cpp b/src/modules/m_opermotd.cpp index 989f97689..bd1853d43 100644 --- a/src/modules/m_opermotd.cpp +++ b/src/modules/m_opermotd.cpp @@ -22,8 +22,6 @@ #include "inspircd.h" -/* $ModDesc: Shows a message to opers after oper-up, adds /opermotd */ - /** Handle /OPERMOTD */ class CommandOpermotd : public Command @@ -82,34 +80,32 @@ class ModuleOpermotd : public Module { } - 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); diff --git a/src/modules/m_operprefix.cpp b/src/modules/m_operprefix.cpp index 62ddb6c67..3bf4c8434 100644 --- a/src/modules/m_operprefix.cpp +++ b/src/modules/m_operprefix.cpp @@ -22,90 +22,52 @@ * 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') { 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; + prefixrank = OPERPREFIX_VALUE; } - - 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) + : opm(this), hideoperwatcher(this) + , hideopermode(this, "hideoper") { - } - - void init() - { - 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; } @@ -114,40 +76,23 @@ class ModuleOperPrefixMode : public Module { std::vector<std::string> modechange; modechange.push_back(""); - modechange.push_back(add ? "+y" : "-y"); + modechange.push_back(add ? "+" : "-"); + modechange[1].push_back(opm.GetModeChar()); 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); + modechange[0] = (*v)->chan->name; + ServerInstance->Modes->Process(modechange, ServerInstance->FakeClient); } } - 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); } @@ -160,10 +105,16 @@ class ModuleOperPrefixMode : public Module } }; -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); } diff --git a/src/modules/m_override.cpp b/src/modules/m_override.cpp index 3e42c4f79..04c5166c0 100644 --- a/src/modules/m_override.cpp +++ b/src/modules/m_override.cpp @@ -26,12 +26,14 @@ #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) { @@ -47,28 +49,46 @@ class ModuleOverride : public Module 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) @@ -80,11 +100,11 @@ class ModuleOverride : public Module } - 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); } @@ -96,9 +116,9 @@ class ModuleOverride : public Module 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)) @@ -110,11 +130,11 @@ class ModuleOverride : public Module 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, const std::vector<std::string>& parameters) 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; unsigned int mode = channel->GetPrefixValue(source); @@ -133,81 +153,33 @@ class ModuleOverride : public Module 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 (!IS_LOCAL(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); } diff --git a/src/modules/m_passforward.cpp b/src/modules/m_passforward.cpp index c04b306b1..b5696c8f4 100644 --- a/src/modules/m_passforward.cpp +++ b/src/modules/m_passforward.cpp @@ -17,29 +17,19 @@ */ -/* $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"); @@ -82,7 +72,7 @@ class ModulePassForward : public Module } } - virtual void OnPostConnect(User* ruser) + void OnPostConnect(User* ruser) CXX11_OVERRIDE { LocalUser* user = IS_LOCAL(ruser); if (!user || user->password.empty()) @@ -92,7 +82,7 @@ class ModulePassForward : public Module { /* 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; } diff --git a/src/modules/m_password_hash.cpp b/src/modules/m_password_hash.cpp index 98462780b..0e3afb966 100644 --- a/src/modules/m_password_hash.cpp +++ b/src/modules/m_password_hash.cpp @@ -18,10 +18,8 @@ */ -/* $ModDesc: Allows for hashed oper passwords */ - #include "inspircd.h" -#include "hash.h" +#include "modules/hash.h" /* Handle /MKPASSWD */ @@ -42,15 +40,14 @@ class CommandMkpasswd : public Command 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; } std::string salt = ServerInstance->GenRandomStr(6, 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); @@ -58,12 +55,11 @@ class CommandMkpasswd : public Command { /* 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()); + 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"); } } @@ -84,17 +80,7 @@ class ModuleOperHash : public Module { } - void init() - { - /* 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) + ModResult OnPassCompare(Extensible* ex, const std::string &data, const std::string &input, const std::string &hashtype) CXX11_OVERRIDE { if (hashtype.substr(0,5) == "hmac-") { @@ -132,7 +118,7 @@ class ModuleOperHash : public Module return MOD_RES_PASSTHRU; } - virtual Version GetVersion() + Version GetVersion() CXX11_OVERRIDE { return Version("Allows for hashed oper passwords",VF_VENDOR); } diff --git a/src/modules/m_permchannels.cpp b/src/modules/m_permchannels.cpp index 69a282637..d23af04bc 100644 --- a/src/modules/m_permchannels.cpp +++ b/src/modules/m_permchannels.cpp @@ -19,154 +19,142 @@ #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->Logs->Log(MODNAME, LOG_DEFAULT, "Cannot create database! %s (%d)", strerror(errno), errno); ServerInstance->SNO->WriteToSnoMask('a', "database: cannot create new db: %s (%d)", 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->Logs->Log(MODNAME, LOG_DEFAULT, "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); return false; } + stream.close(); #ifdef _WIN32 if (remove(permchannelsconf.c_str())) { - ServerInstance->Logs->Log("m_permchannels",DEFAULT, "permchannels: Cannot remove old database! %s (%d)", strerror(errno), errno); + ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Cannot remove old database! %s (%d)", strerror(errno), errno); ServerInstance->SNO->WriteToSnoMask('a', "database: cannot remove old database: %s (%d)", strerror(errno), errno); return false; } #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->Logs->Log(MODNAME, LOG_DEFAULT, "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); return false; } @@ -174,42 +162,6 @@ static bool WriteDatabase(Module* mod, bool save_listmodes) 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; @@ -223,46 +175,14 @@ public: { } - 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() @@ -276,12 +196,11 @@ public: { 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; } @@ -289,19 +208,23 @@ public: 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; @@ -330,24 +253,24 @@ public: } } - 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; } @@ -366,7 +289,7 @@ public: // 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) { @@ -376,38 +299,19 @@ public: } 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; diff --git a/src/modules/m_randquote.cpp b/src/modules/m_randquote.cpp index 668eea0e5..1bb28583e 100644 --- a/src/modules/m_randquote.cpp +++ b/src/modules/m_randquote.cpp @@ -21,80 +21,37 @@ */ -/* $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); } }; diff --git a/src/modules/m_redirect.cpp b/src/modules/m_redirect.cpp index 26d6b162b..e822676bf 100644 --- a/src/modules/m_redirect.cpp +++ b/src/modules/m_redirect.cpp @@ -24,66 +24,51 @@ #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; } }; @@ -92,75 +77,62 @@ class Redirect : public ModeHandler 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; } } @@ -169,11 +141,7 @@ class ModuleRedirect : public Module 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); } diff --git a/src/modules/m_regex_glob.cpp b/src/modules/m_regex_glob.cpp index 44d1a5898..9c3162885 100644 --- a/src/modules/m_regex_glob.cpp +++ b/src/modules/m_regex_glob.cpp @@ -18,11 +18,9 @@ */ -#include "m_regex.h" +#include "modules/regex.h" #include "inspircd.h" -/* $ModDesc: Regex module using plain wildcard matching. */ - class GlobRegex : public Regex { public: @@ -30,11 +28,7 @@ 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); } @@ -43,7 +37,7 @@ public: class GlobFactory : public RegexFactory { public: - Regex* Create(const std::string& expr) + Regex* Create(const std::string& expr) CXX11_OVERRIDE { return new GlobRegex(expr); } @@ -55,11 +49,12 @@ class ModuleRegexGlob : public Module { 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); } diff --git a/src/modules/m_regonlycreate.cpp b/src/modules/m_regonlycreate.cpp index 61f94c0bd..0ffe5e085 100644 --- a/src/modules/m_regonlycreate.cpp +++ b/src/modules/m_regonlycreate.cpp @@ -21,28 +21,27 @@ #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(); @@ -50,15 +49,11 @@ class ModuleRegOnlyCreate : public Module 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); } diff --git a/src/modules/m_remove.cpp b/src/modules/m_remove.cpp index cf139f4a3..ed9b6ce25 100644 --- a/src/modules/m_remove.cpp +++ b/src/modules/m_remove.cpp @@ -24,8 +24,6 @@ #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. @@ -36,12 +34,14 @@ */ 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) + RemoveBase(Module* Creator, bool& snk, ChanModeReference& nkm, const char* cmdn) + : Command(Creator, cmdn, 2, 3) + , supportnokicks(snk) + , nokicksmode(nkm) { } @@ -50,9 +50,6 @@ class RemoveBase : public Command User* target; Channel* channel; std::string reason; - std::string protectkey; - std::string founderkey; - bool hasnokicks; /* 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. @@ -74,7 +71,7 @@ class RemoveBase : public Command /* 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; } @@ -87,16 +84,14 @@ class RemoveBase : public Command 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) @@ -121,7 +116,7 @@ class RemoveBase : public Command 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); } @@ -134,13 +129,13 @@ class RemoveBase : public Command 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; + RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) = 0; }; /** Handle /REMOVE @@ -148,11 +143,11 @@ class RemoveBase : public Command 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); + TRANSLATE3(TR_NICK, TR_TEXT, TR_TEXT); } CmdResult Handle (const std::vector<std::string>& parameters, User *user) @@ -174,11 +169,11 @@ class CommandRemove : public RemoveBase 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) @@ -197,44 +192,33 @@ class CommandFpart : public RemoveBase 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"); } - 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) diff --git a/src/modules/m_repeat.cpp b/src/modules/m_repeat.cpp new file mode 100644 index 000000000..d8fccbffc --- /dev/null +++ b/src/modules/m_repeat.cpp @@ -0,0 +1,403 @@ +/* + * 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 UserMembList* users = chan->GetUsers(); + for (UserMembCIter 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) + { + std::vector<std::string> parameters; + parameters.push_back(memb->chan->name); + parameters.push_back("+b"); + parameters.push_back("*!*@" + user->dhost); + ServerInstance->Modes->Process(parameters, ServerInstance->FakeClient); + } + + 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) diff --git a/src/modules/m_restrictchans.cpp b/src/modules/m_restrictchans.cpp index c76b0e79f..b619ee448 100644 --- a/src/modules/m_restrictchans.cpp +++ b/src/modules/m_restrictchans.cpp @@ -22,13 +22,12 @@ #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"); @@ -36,48 +35,26 @@ class ModuleRestrictChans : public Module { 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); } diff --git a/src/modules/m_restrictmsg.cpp b/src/modules/m_restrictmsg.cpp index e814f3b16..e0887e587 100644 --- a/src/modules/m_restrictmsg.cpp +++ b/src/modules/m_restrictmsg.cpp @@ -21,22 +21,10 @@ #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))) { @@ -46,11 +34,11 @@ class ModuleRestrictMsg : public Module // (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; } @@ -58,16 +46,7 @@ class ModuleRestrictMsg : public Module 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); } diff --git a/src/modules/m_ripemd160.cpp b/src/modules/m_ripemd160.cpp index 04c27e83d..261cd1e27 100644 --- a/src/modules/m_ripemd160.cpp +++ b/src/modules/m_ripemd160.cpp @@ -56,25 +56,15 @@ */ -/* $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) \ @@ -167,7 +157,6 @@ class RIProv : public HashProvider { 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]; @@ -176,7 +165,6 @@ class RIProv : public HashProvider } else { - ServerInstance->Logs->Log("m_ripemd160.so", DEBUG, "initialize with default mdbuf"); MDbuf[0] = 0x67452301UL; MDbuf[1] = 0xefcdab89UL; MDbuf[2] = 0x98badcfeUL; @@ -417,7 +405,6 @@ class RIProv : public HashProvider 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 */ @@ -453,11 +440,6 @@ public: 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) {} }; @@ -467,15 +449,12 @@ 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) - diff --git a/src/modules/m_rline.cpp b/src/modules/m_rline.cpp index d1ab5d9ba..2aee89ad2 100644 --- a/src/modules/m_rline.cpp +++ b/src/modules/m_rline.cpp @@ -20,10 +20,8 @@ */ -/* $ModDesc: RLINE: Regexp user banning. */ - #include "inspircd.h" -#include "m_regex.h" +#include "modules/regex.h" #include "xline.h" static bool ZlineOnMatch = false; @@ -60,7 +58,8 @@ class RLine : public XLine 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; @@ -79,7 +78,7 @@ class RLine : public XLine 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; @@ -90,15 +89,9 @@ class RLine : public XLine 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; @@ -116,7 +109,7 @@ class RLineFactory : public XLineFactory 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) @@ -129,10 +122,6 @@ class RLineFactory : public XLineFactory return new RLine(set_time, duration, source, reason, xline_specific_mask, rxfactory); } - - ~RLineFactory() - { - } }; /** Handle /RLINE @@ -156,7 +145,7 @@ class CommandRLine : public Command { // 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 @@ -165,7 +154,7 @@ class CommandRLine : public Command } 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) @@ -179,7 +168,7 @@ class CommandRLine : public Command 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()); } @@ -188,7 +177,7 @@ class CommandRLine : public Command 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"); } } } @@ -200,7 +189,7 @@ class CommandRLine : public Command } 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."); } } @@ -218,7 +207,6 @@ class CommandRLine : public Command class ModuleRLine : public Module { - private: dynamic_reference<RegexFactory> rxfactory; RLineFactory f; CommandRLine r; @@ -233,29 +221,23 @@ class ModuleRLine : public Module { } - 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); @@ -269,7 +251,7 @@ class ModuleRLine : public Module return MOD_RES_PASSTHRU; } - virtual void OnRehash(User *user) + void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE { ConfigTag* tag = ServerInstance->Config->ConfValue("rline"); @@ -302,7 +284,7 @@ class ModuleRLine : public Module 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; @@ -311,7 +293,7 @@ class ModuleRLine : public Module 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; @@ -328,7 +310,7 @@ class ModuleRLine : public Module } } - virtual void OnBackgroundTimer(time_t curtime) + void OnBackgroundTimer(time_t curtime) CXX11_OVERRIDE { if (added_zline) { @@ -337,7 +319,7 @@ class ModuleRLine : public Module } } - void OnUnloadModule(Module* mod) + void OnUnloadModule(Module* mod) CXX11_OVERRIDE { // If the regex engine became unavailable or has changed, remove all rlines if (!rxfactory) diff --git a/src/modules/m_rmode.cpp b/src/modules/m_rmode.cpp new file mode 100644 index 000000000..dde9f496e --- /dev/null +++ b/src/modules/m_rmode.cpp @@ -0,0 +1,116 @@ +/* + * 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; + irc::modestacker modestack(false); + + if ((pm = mh->IsPrefixMode())) + { + // As user prefix modes don't have a GetList() method, let's iterate through the channel's users. + for (UserMembIter it = chan->userlist.begin(); it != chan->userlist.end(); ++it) + { + if (!InspIRCd::Match(it->first->nick, pattern)) + continue; + if (it->second->hasMode(modeletter) && !((it->first == user) && (pm->GetPrefixRank() > VOICE_VALUE))) + modestack.Push(modeletter, 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; + modestack.Push(modeletter, it->mask); + } + } + else + { + if (chan->IsModeSet(mh)) + modestack.Push(modeletter); + } + + parameterlist stackresult; + stackresult.push_back(chan->name); + while (modestack.GetStackedLine(stackresult)) + { + ServerInstance->Modes->Process(stackresult, user); + stackresult.erase(stackresult.begin() + 1, stackresult.end()); + } + + 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) diff --git a/src/modules/m_sajoin.cpp b/src/modules/m_sajoin.cpp index 7ac465732..406d9ffcd 100644 --- a/src/modules/m_sajoin.cpp +++ b/src/modules/m_sajoin.cpp @@ -21,62 +21,60 @@ #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>"; + TRANSLATE2(TR_NICK, TR_TEXT); } CmdResult Handle (const std::vector<std::string>& parameters, User *user) { - User* dest = ServerInstance->FindNick(parameters[0]); + const std::string& channel = parameters.size() > 1 ? parameters[1] : parameters[0]; + 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->WriteNotice("*** You are not allowed to /SAJOIN other users (the privilege users/sajoin-others is needed to /SAJOIN others)."); + return CMD_FAILURE; + } + + 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; } - if (IS_LOCAL(user) && !ServerInstance->IsChannel(parameters[1].c_str(), ServerInstance->Config->Limits.ChanMax)) + 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; } - /* 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) + Channel* n = Channel::JoinUser(localuser, channel, true); + if (n && n->HasUser(dest)) { - 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; } } @@ -87,7 +85,7 @@ class CommandSajoin : public Command } else { - user->WriteServ("NOTICE "+user->nick+" :*** No such nickname "+parameters[0]); + user->WriteNotice("*** No such nickname "+nickname); return CMD_FAILURE; } } @@ -110,20 +108,10 @@ class ModuleSajoin : public Module { } - 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) diff --git a/src/modules/m_sakick.cpp b/src/modules/m_sakick.cpp index 7dfcd8904..911b826dc 100644 --- a/src/modules/m_sakick.cpp +++ b/src/modules/m_sakick.cpp @@ -20,8 +20,6 @@ #include "inspircd.h" -/* $ModDesc: Provides a SAKICK command */ - /** Handle /SAKICK */ class CommandSakick : public Command @@ -30,29 +28,27 @@ 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; } @@ -62,28 +58,16 @@ class CommandSakick : public Command */ 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; @@ -107,21 +91,10 @@ class ModuleSakick : public Module { } - 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) - diff --git a/src/modules/m_samode.cpp b/src/modules/m_samode.cpp index ea2ae24ab..14f79aaf7 100644 --- a/src/modules/m_samode.cpp +++ b/src/modules/m_samode.cpp @@ -20,8 +20,6 @@ */ -/* $ModDesc: Provides command SAMODE to allow opers to change modes on channels and users */ - #include "inspircd.h" /** Handle /SAMODE @@ -48,9 +46,14 @@ class CommandSamode : public Command return CMD_FAILURE; } } - + User* target = ServerInstance->FindNick(parameters[0]); + if ((target) && (target != user)) + { + if (!user->HasPrivPermission("users/samode-usermodes", true)) + return CMD_FAILURE; + } this->active = true; - ServerInstance->Parser->CallHandler("MODE", parameters, user); + ServerInstance->Modes->Process(parameters, user); if (ServerInstance->Modes->GetLastParse().length()) ServerInstance->SNO->WriteGlobalSno('a', user->nick + " used SAMODE: " +ServerInstance->Modes->GetLastParse()); this->active = false; @@ -67,22 +70,12 @@ class ModuleSaMode : public Module { } - 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, const std::vector<std::string>& parameters) CXX11_OVERRIDE { if (cmd.active) return MOD_RES_ALLOW; diff --git a/src/modules/m_sanick.cpp b/src/modules/m_sanick.cpp index 4e4be77ae..ca6be2211 100644 --- a/src/modules/m_sanick.cpp +++ b/src/modules/m_sanick.cpp @@ -21,8 +21,6 @@ #include "inspircd.h" -/* $ModDesc: Provides support for SANICK command */ - /** Handle /SANICK */ class CommandSanick : public Command @@ -32,7 +30,7 @@ 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) @@ -42,21 +40,21 @@ class CommandSanick : public Command /* 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; } } @@ -98,20 +96,10 @@ class ModuleSanick : public Module { } - 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) diff --git a/src/modules/m_sapart.cpp b/src/modules/m_sapart.cpp index 89256e0e4..fa5ab176a 100644 --- a/src/modules/m_sapart.cpp +++ b/src/modules/m_sapart.cpp @@ -21,8 +21,6 @@ #include "inspircd.h" -/* $ModDesc: Provides command SAPART to force-part users from a channel. */ - /** Handle /SAPART */ class CommandSapart : public Command @@ -31,7 +29,7 @@ class CommandSapart : public Command 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); + TRANSLATE3(TR_NICK, TR_TEXT, TR_TEXT); } CmdResult Handle (const std::vector<std::string>& parameters, User *user) @@ -45,9 +43,15 @@ class CommandSapart : public Command if (parameters.size() > 2) reason = parameters[2]; - 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; + } + + if (!channel->HasUser(dest)) + { + user->WriteNotice("*** " + dest->nick + " is not on " + channel->name); return CMD_FAILURE; } @@ -58,33 +62,14 @@ class CommandSapart : public Command 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; @@ -109,21 +94,10 @@ class ModuleSapart : public Module { } - 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) - diff --git a/src/modules/m_saquit.cpp b/src/modules/m_saquit.cpp index 909a026ab..aa6aa0180 100644 --- a/src/modules/m_saquit.cpp +++ b/src/modules/m_saquit.cpp @@ -21,8 +21,6 @@ #include "inspircd.h" -/* $ModDesc: Provides support for an SAQUIT command, exits user with a reason */ - /** Handle /SAQUIT */ class CommandSaquit : public Command @@ -31,7 +29,7 @@ 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) @@ -39,16 +37,16 @@ class CommandSaquit : public Command 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]); @@ -56,7 +54,7 @@ class CommandSaquit : public Command } 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; } } @@ -79,20 +77,10 @@ class ModuleSaquit : public Module { } - 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) diff --git a/src/modules/m_sasl.cpp b/src/modules/m_sasl.cpp index 66efcfe4e..074362651 100644 --- a/src/modules/m_sasl.cpp +++ b/src/modules/m_sasl.cpp @@ -19,12 +19,10 @@ #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 }; @@ -33,7 +31,7 @@ static std::string sasl_target = "*"; static void SendSASL(const parameterlist& params) { - if (!ServerInstance->PI->SendEncapsulatedData(params)) + if (!ServerInstance->PI->SendEncapsulatedData(sasl_target, "SASL", params)) { SASLFallback(NULL, params); } @@ -56,17 +54,15 @@ class SaslAuthenticator : 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); @@ -109,13 +105,13 @@ class SaslAuthenticator 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; } @@ -134,8 +130,6 @@ class SaslAuthenticator 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"); @@ -161,13 +155,13 @@ class SaslAuthenticator 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; @@ -223,7 +217,7 @@ class CommandSASL : public Command 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; } @@ -252,31 +246,25 @@ class ModuleSASL : public Module 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_) @@ -288,12 +276,12 @@ class ModuleSASL : public Module 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); } diff --git a/src/modules/m_satopic.cpp b/src/modules/m_satopic.cpp index ae1c19d91..4a6f85536 100644 --- a/src/modules/m_satopic.cpp +++ b/src/modules/m_satopic.cpp @@ -17,8 +17,6 @@ */ -/* $ModDesc: Provides a SATOPIC command */ - #include "inspircd.h" /** Handle /SATOPIC @@ -40,17 +38,15 @@ class CommandSATopic : public Command 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; } } @@ -65,16 +61,7 @@ class ModuleSATopic : public Module { } - void init() - { - ServerInstance->Modules->AddService(cmd); - } - - virtual ~ModuleSATopic() - { - } - - virtual Version GetVersion() + Version GetVersion() CXX11_OVERRIDE { return Version("Provides a SATOPIC command", VF_VENDOR); } diff --git a/src/modules/m_securelist.cpp b/src/modules/m_securelist.cpp index 6013d1fd7..f4042b8f6 100644 --- a/src/modules/m_securelist.cpp +++ b/src/modules/m_securelist.cpp @@ -21,31 +21,18 @@ #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(); @@ -61,13 +48,13 @@ class ModuleSecureList : public Module * 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++) @@ -75,20 +62,20 @@ class ModuleSecureList : public Module 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"]; } }; diff --git a/src/modules/m_seenicks.cpp b/src/modules/m_seenicks.cpp index 95872b5b2..bff3516f1 100644 --- a/src/modules/m_seenicks.cpp +++ b/src/modules/m_seenicks.cpp @@ -21,24 +21,20 @@ #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()); } diff --git a/src/modules/m_serverban.cpp b/src/modules/m_serverban.cpp index cf77ae9ba..f51d1d373 100644 --- a/src/modules/m_serverban.cpp +++ b/src/modules/m_serverban.cpp @@ -19,43 +19,28 @@ #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) - diff --git a/src/modules/m_services_account.cpp b/src/modules/m_services_account.cpp index 154968e9e..feb62f858 100644 --- a/src/modules/m_services_account.cpp +++ b/src/modules/m_services_account.cpp @@ -22,10 +22,8 @@ */ -/* $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 */ @@ -40,15 +38,15 @@ class Channel_r : public ModeHandler 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; } @@ -66,15 +64,15 @@ class User_r : public ModeHandler { 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; } @@ -104,86 +102,118 @@ class AChannel_M : public SimpleChannelModeHandler AChannel_M(Module* Creator) : SimpleChannelModeHandler(Creator, "regmoderated", 'M') { } }; -class ModuleServicesAccount : public Module +static bool ReadCGIIRCExt(const char* extname, User* user, const std::string*& out) { - AChannel_R m1; - AChannel_M m2; - AUser_R m3; - Channel_r m4; - User_r m5; - AccountExtItem accountname; - bool checking_ban; + ExtensionItem* wiext = ServerInstance->Extensions.GetItem(extname); + if (!wiext) + return false; - 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; + 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; + StringExtItem* stringext = static_cast<StringExtItem*>(wiext); + std::string* addr = stringext->get(user); + if (!addr) + return false; - out = addr; - return true; - } + out = addr; + return true; +} +class AccountExtItemImpl : public AccountExtItem +{ public: - ModuleServicesAccount() : m1(this), m2(this), m3(this), m4(this), m5(this), - accountname("accountname", this), checking_ban(false) + AccountExtItemImpl(Module* mod) + : AccountExtItem("accountname", mod) { } - void init() + void unserialize(SerializeFormat format, Extensible* container, const std::string& value) { - 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 }; + User* user = dynamic_cast<User*>(container); + if (!user) + return; - ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation)); + StringExtItem::unserialize(format, container, value); + if (!value.empty()) + { + // Logged in + if (IS_LOCAL(user)) + { + const std::string* host = &user->dhost; + if (user->registered != REG_ALL) + { + if (!ReadCGIIRCExt("cgiirc_webirc_hostname", user, host)) + { + ReadCGIIRCExt("cgiirc_webirc_ip", user, host); + } + } + + user->WriteNumeric(900, "%s!%s@%s %s :You are now logged in as %s", + user->nick.c_str(), user->ident.c_str(), host->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; + AChannel_M m2; + AUser_R m3; + Channel_r m4; + User_r m5; + AccountExtItemImpl accountname; + bool checking_ban; + public: + ModuleServicesAccount() : m1(this), m2(this), m3(this), m4(this), m5(this), + accountname(this) + { } - void On005Numeric(std::string &t) + void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE { - 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) + if (user->IsModeSet(m5) && assign(user->nick) != oldnick) { std::vector<std::string> modechange; modechange.push_back(user->nick); modechange.push_back("-r"); - ServerInstance->SendMode(modechange, ServerInstance->FakeClient); + ServerInstance->Modes->Process(modechange, ServerInstance->FakeClient, ModeParser::MODE_LOCALONLY); } } - 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; @@ -196,10 +226,10 @@ class ModuleServicesAccount : public Module 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; } } @@ -207,17 +237,17 @@ class ModuleServicesAccount : public Module { 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; @@ -253,27 +283,19 @@ class ModuleServicesAccount : public Module return MOD_RES_PASSTHRU; } - 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); - } - - 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)) - 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; } } @@ -281,56 +303,14 @@ class ModuleServicesAccount : public Module 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); } diff --git a/src/modules/m_servprotect.cpp b/src/modules/m_servprotect.cpp index b4f2b5bbd..26453020f 100644 --- a/src/modules/m_servprotect.cpp +++ b/src/modules/m_servprotect.cpp @@ -21,8 +21,6 @@ #include "inspircd.h" -/* $ModDesc: Provides usermode +k to protect services from kicks, kills and mode changes. */ - /** Handles user mode +k */ class ServProtectMode : public ModeHandler @@ -53,37 +51,25 @@ class ModuleServProtectMode : public Module { } - 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 */ @@ -95,10 +81,10 @@ class ModuleServProtectMode : public Module * 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; } } @@ -107,37 +93,36 @@ class ModuleServProtectMode : public Module 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) diff --git a/src/modules/m_sethost.cpp b/src/modules/m_sethost.cpp index 2ef0c0548..75dbe1c6a 100644 --- a/src/modules/m_sethost.cpp +++ b/src/modules/m_sethost.cpp @@ -21,20 +21,17 @@ #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) @@ -44,18 +41,18 @@ class CommandSethost : public Command { 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; @@ -70,21 +67,14 @@ class ModuleSetHost : public Module { 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"); @@ -93,15 +83,10 @@ class ModuleSetHost : public Module 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) diff --git a/src/modules/m_setident.cpp b/src/modules/m_setident.cpp index f63be1381..93dd4c332 100644 --- a/src/modules/m_setident.cpp +++ b/src/modules/m_setident.cpp @@ -22,8 +22,6 @@ #include "inspircd.h" -/* $ModDesc: Provides support for the SETIDENT command */ - /** Handle /SETIDENT */ class CommandSetident : public Command @@ -33,31 +31,29 @@ 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; @@ -67,21 +63,10 @@ class ModuleSetIdent : public Module { } - 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) diff --git a/src/modules/m_setidle.cpp b/src/modules/m_setidle.cpp index fdb29d14f..dd82aef29 100644 --- a/src/modules/m_setidle.cpp +++ b/src/modules/m_setidle.cpp @@ -21,25 +21,22 @@ #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); @@ -47,7 +44,7 @@ class CommandSetidle : public Command 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; } @@ -63,16 +60,7 @@ class ModuleSetIdle : public Module { } - 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); } diff --git a/src/modules/m_setname.cpp b/src/modules/m_setname.cpp index d0610853b..0e71840f7 100644 --- a/src/modules/m_setname.cpp +++ b/src/modules/m_setname.cpp @@ -21,8 +21,6 @@ #include "inspircd.h" -/* $ModDesc: Provides support for the SETNAME command */ - class CommandSetname : public Command @@ -32,18 +30,17 @@ 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()); } @@ -62,16 +59,7 @@ class ModuleSetName : public Module { } - 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); } diff --git a/src/modules/m_sha256.cpp b/src/modules/m_sha256.cpp index 86970968a..d2755bacc 100644 --- a/src/modules/m_sha256.cpp +++ b/src/modules/m_sha256.cpp @@ -56,17 +56,8 @@ * 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) @@ -263,11 +254,6 @@ class HashSHA256 : public HashProvider 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) {} }; @@ -277,10 +263,9 @@ 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); } diff --git a/src/modules/m_showfile.cpp b/src/modules/m_showfile.cpp new file mode 100644 index 000000000..c42877eef --- /dev/null +++ b/src/modules/m_showfile.cpp @@ -0,0 +1,169 @@ +/* + * 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) diff --git a/src/modules/m_showwhois.cpp b/src/modules/m_showwhois.cpp index 398ebf571..ba17942cb 100644 --- a/src/modules/m_showwhois.cpp +++ b/src/modules/m_showwhois.cpp @@ -23,16 +23,19 @@ #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; } }; @@ -46,9 +49,9 @@ class WhoisNoticeCmd : public Command 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) @@ -69,46 +72,35 @@ class WhoisNoticeCmd : public Command 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)) @@ -118,14 +110,11 @@ class ModuleShowwhois : public Module 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) diff --git a/src/modules/m_shun.cpp b/src/modules/m_shun.cpp index 8bf4d30e7..075b80eb7 100644 --- a/src/modules/m_shun.cpp +++ b/src/modules/m_shun.cpp @@ -23,8 +23,6 @@ #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: @@ -36,14 +34,11 @@ 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)) @@ -59,15 +54,9 @@ public: 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; } }; @@ -107,7 +96,7 @@ class CommandShun : public Command /* '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(); @@ -120,18 +109,18 @@ class CommandShun : public Command } 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 @@ -151,7 +140,7 @@ class CommandShun : public Command 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()); } @@ -159,7 +148,7 @@ class CommandShun : public Command 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; } } @@ -188,17 +177,12 @@ class ModuleShun : public Module { } - 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); @@ -210,7 +194,7 @@ class ModuleShun : public Module 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; @@ -219,7 +203,7 @@ class ModuleShun : public Module 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"); @@ -242,7 +226,7 @@ class ModuleShun : public Module 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; @@ -253,7 +237,7 @@ class ModuleShun : public Module 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; @@ -264,7 +248,7 @@ class ModuleShun : public Module 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; } @@ -283,11 +267,10 @@ class ModuleShun : public Module 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) - diff --git a/src/modules/m_silence.cpp b/src/modules/m_silence.cpp index c82ab3f9d..3a213c6e7 100644 --- a/src/modules/m_silence.cpp +++ b/src/modules/m_silence.cpp @@ -23,8 +23,6 @@ #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> * @@ -66,7 +64,7 @@ class CommandSVSSilence : public Command 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) @@ -78,7 +76,7 @@ class CommandSVSSilence : public Command * 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]); @@ -112,7 +110,6 @@ class CommandSilence : public Command { 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) @@ -127,10 +124,10 @@ class CommandSilence : public Command 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; } @@ -149,7 +146,7 @@ class CommandSilence : public Command if (pattern == 0) { - user->WriteServ("NOTICE %s :Bad SILENCE pattern",user->nick.c_str()); + user->WriteNotice("Bad SILENCE pattern"); return CMD_INVALID; } @@ -176,7 +173,7 @@ class CommandSilence : public Command 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); @@ -185,7 +182,7 @@ class CommandSilence : public Command } } } - 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 == '+') { @@ -198,7 +195,7 @@ class CommandSilence : public Command } 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; } @@ -208,7 +205,7 @@ class CommandSilence : public Command 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; } } @@ -220,7 +217,7 @@ class CommandSilence : public Command { 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; } } @@ -302,28 +299,17 @@ class ModuleSilence : public Module { } - 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) @@ -343,44 +329,27 @@ class ModuleSilence : public Module } } - 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) { @@ -393,11 +362,7 @@ class ModuleSilence : public Module return MOD_RES_PASSTHRU; } - ~ModuleSilence() - { - } - - Version GetVersion() + Version GetVersion() CXX11_OVERRIDE { return Version("Provides support for the /SILENCE command", VF_OPTCOMMON | VF_VENDOR); } diff --git a/src/modules/m_spanningtree/addline.cpp b/src/modules/m_spanningtree/addline.cpp index 16043b2aa..dbf0003bf 100644 --- a/src/modules/m_spanningtree/addline.cpp +++ b/src/modules/m_spanningtree/addline.cpp @@ -20,60 +20,37 @@ #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()); } @@ -82,20 +59,29 @@ bool TreeSocket::AddLine(const std::string &prefix, parameterlist ¶ms) 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) { 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); +} diff --git a/src/modules/m_spanningtree/away.cpp b/src/modules/m_spanningtree/away.cpp index ed97c48cd..9c4ec5783 100644 --- a/src/modules/m_spanningtree/away.cpp +++ b/src/modules/m_spanningtree/away.cpp @@ -21,32 +21,38 @@ #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[params.size() - 1])); 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]; } 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); } diff --git a/src/modules/m_spanningtree/cachetimer.cpp b/src/modules/m_spanningtree/cachetimer.cpp deleted file mode 100644 index be438651d..000000000 --- a/src/modules/m_spanningtree/cachetimer.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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(); -} - diff --git a/src/modules/m_spanningtree/cachetimer.h b/src/modules/m_spanningtree/cachetimer.h index bad1b7419..89933cc4b 100644 --- a/src/modules/m_spanningtree/cachetimer.h +++ b/src/modules/m_spanningtree/cachetimer.h @@ -17,13 +17,7 @@ */ -#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 @@ -31,11 +25,7 @@ class SpanningTreeUtilities; */ class CacheRefreshTimer : public Timer { - private: - SpanningTreeUtilities *Utils; public: - CacheRefreshTimer(SpanningTreeUtilities* Util); - virtual void Tick(time_t TIME); + CacheRefreshTimer(); + bool Tick(time_t TIME); }; - -#endif diff --git a/src/modules/m_spanningtree/capab.cpp b/src/modules/m_spanningtree/capab.cpp index 62afe5974..6542a1f3a 100644 --- a/src/modules/m_spanningtree/capab.cpp +++ b/src/modules/m_spanningtree/capab.cpp @@ -20,9 +20,7 @@ #include "inspircd.h" -#include "xline.h" -#include "treesocket.h" #include "treeserver.h" #include "utils.h" #include "link.h" @@ -30,27 +28,22 @@ 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; @@ -66,16 +59,18 @@ static std::string BuildModeList(ModeType type) { 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(); + return irc::stringjoiner(modes); } void TreeSocket::SendCapabilities(int phase) @@ -90,7 +85,7 @@ 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 */ @@ -139,8 +134,10 @@ void TreeSocket::SendCapabilities(int phase) 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)+ @@ -152,12 +149,12 @@ void TreeSocket::SendCapabilities(int phase) " 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)+ - " SVSPART=1"); + " CHANMODES="+ServerInstance->Modes->GiveModeList(MODETYPE_CHANNEL)+ + " USERMODES="+ServerInstance->Modes->GiveModeList(MODETYPE_USER) + ); this->WriteLine("CAPAB END"); } @@ -202,7 +199,23 @@ bool TreeSocket::Capab(const parameterlist ¶ms) 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") @@ -212,7 +225,7 @@ bool TreeSocket::Capab(const parameterlist ¶ms) 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."; @@ -250,21 +263,6 @@ bool TreeSocket::Capab(const parameterlist ¶ms) } } - 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."; @@ -286,7 +284,7 @@ bool TreeSocket::Capab(const parameterlist ¶ms) } 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."; } @@ -308,7 +306,7 @@ bool TreeSocket::Capab(const parameterlist ¶ms) } 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."; } @@ -348,7 +346,7 @@ bool TreeSocket::Capab(const parameterlist ¶ms) } else { - capab->ModuleList.push_back(proto_version > 1201 ? ' ' : ','); + capab->ModuleList.push_back(' '); capab->ModuleList.append(params[1]); } } @@ -390,4 +388,3 @@ bool TreeSocket::Capab(const parameterlist ¶ms) } return true; } - diff --git a/src/modules/m_spanningtree/commandbuilder.h b/src/modules/m_spanningtree/commandbuilder.h new file mode 100644 index 000000000..cd627227a --- /dev/null +++ b/src/modules/m_spanningtree/commandbuilder.h @@ -0,0 +1,140 @@ +/* + * 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; + } + + 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); + } +}; diff --git a/src/modules/m_spanningtree/commands.h b/src/modules/m_spanningtree/commands.h index 3b5b499c1..0c1ea8e49 100644 --- a/src/modules/m_spanningtree/commands.h +++ b/src/modules/m_spanningtree/commands.h @@ -17,126 +17,321 @@ */ -#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); + static void ApplyModeStack(User* srcuser, Channel* c, irc::modestacker& stack); + + /** + * 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); + bool ProcessModeUUIDPair(const std::string& item, TreeSocket* src_socket, Channel* chan, irc::modestacker* modestack); + public: + CommandFJoin(Module* Creator) : ServerCommand(Creator, "FJOIN", 3) { } + CmdResult Handle(User* user, std::vector<std::string>& params); }; -class CommandFMode : public Command + +class CommandFMode : public ServerCommand { 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; } + CommandFMode(Module* Creator) : ServerCommand(Creator, "FMODE", 3) { } + CmdResult Handle(User* user, std::vector<std::string>& params); }; -class CommandFTopic : public Command + +class CommandFTopic : public ServerCommand { 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; } + 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 Command + +class CommandFHost : public UserOnlyServerCommand<CommandFHost> { 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; } + CommandFHost(Module* Creator) : UserOnlyServerCommand<CommandFHost>(Creator, "FHOST", 1) { } + CmdResult HandleRemote(RemoteUser* user, std::vector<std::string>& params); }; -class CommandFIdent : public Command + +class CommandFIdent : public UserOnlyServerCommand<CommandFIdent> { 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; } + CommandFIdent(Module* Creator) : UserOnlyServerCommand<CommandFIdent>(Creator, "FIDENT", 1) { } + CmdResult HandleRemote(RemoteUser* user, std::vector<std::string>& params); }; -class CommandFName : public Command + +class CommandFName : public UserOnlyServerCommand<CommandFName> { 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; } + CommandFName(Module* Creator) : UserOnlyServerCommand<CommandFName>(Creator, "FNAME", 1) { } + CmdResult HandleRemote(RemoteUser* user, std::vector<std::string>& params); +}; + +class CommandIJoin : public UserOnlyServerCommand<CommandIJoin> +{ + public: + CommandIJoin(Module* Creator) : UserOnlyServerCommand<CommandIJoin>(Creator, "IJOIN", 1) { } + CmdResult HandleRemote(RemoteUser* user, std::vector<std::string>& params); +}; + +class CommandResync : public ServerOnlyServerCommand<CommandResync> +{ + public: + CommandResync(Module* Creator) : ServerOnlyServerCommand<CommandResync>(Creator, "RESYNC", 1) { } + CmdResult HandleServer(TreeServer* server, std::vector<std::string>& parameters); +}; + +class CommandAway : public UserOnlyServerCommand<CommandAway> +{ + public: + 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 XLine; +class CommandAddLine : public ServerCommand +{ + public: + 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 CommandDelLine : public ServerCommand +{ + public: + 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: + CommandSave(Module* Creator) : ServerCommand(Creator, "SAVE", 2) { } + CmdResult Handle(User* user, std::vector<std::string>& parameters); +}; + +class CommandServer : public ServerOnlyServerCommand<CommandServer> +{ + public: + CommandServer(Module* Creator) : ServerOnlyServerCommand<CommandServer>(Creator, "SERVER", 5) { } + CmdResult HandleServer(TreeServer* server, std::vector<std::string>& parameters); + + class Builder : public CmdBuilder + { + 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 CommandVersion : public ServerOnlyServerCommand<CommandVersion> +{ + public: + CommandVersion(Module* Creator) : ServerOnlyServerCommand<CommandVersion>(Creator, "VERSION", 1) { } + CmdResult HandleServer(TreeServer* server, std::vector<std::string>& parameters); +}; + +class CommandBurst : public ServerOnlyServerCommand<CommandBurst> +{ + public: + CommandBurst(Module* Creator) : ServerOnlyServerCommand<CommandBurst>(Creator, "BURST") { } + CmdResult HandleServer(TreeServer* server, 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 SpanningTreeCommands { public: - CommandRConnect rconnect; - CommandRSQuit rsquit; CommandSVSJoin svsjoin; CommandSVSPart svspart; CommandSVSNick svsnick; @@ -144,12 +339,28 @@ class SpanningTreeCommands 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; + CommandVersion version; + CommandBurst burst; + CommandEndBurst endburst; SpanningTreeCommands(ModuleSpanningTree* module); }; - -#endif diff --git a/src/modules/m_spanningtree/compat.cpp b/src/modules/m_spanningtree/compat.cpp index ec0cdb036..1d573b8b4 100644 --- a/src/modules/m_spanningtree/compat.cpp +++ b/src/modules/m_spanningtree/compat.cpp @@ -20,177 +20,283 @@ #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) -{ - 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"); - } - } -} - -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> [<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) + { + // 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 + { + std::string::size_type 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; + + 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 (subcmd == "CHGIDENT" && d != std::string::npos) + if (line[b + 1] == '#') + { + // We're sending channel metadata + line.erase(c, d-c); + } + else if (line.substr(c, d-c) == " operquit") + { + // ":22D METADATA 22DAAAAAX operquit :message" -> ":22DAAAAAX OPERQUIT :message" + line = ":" + line.substr(b+1, c-b) + "OPERQUIT" + line.substr(d); + } + } + else if (command == "FTOPIC") { - 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); + // 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; - ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Forging acceptance of CHGIDENT from 1201-protocol server"); - recvq.insert(0, ":" + target + " FIDENT " + line.substr(e) + "\n"); + 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 (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; - Command* thiscmd = ServerInstance->Parser->GetHandler(subcmd); - if (thiscmd && subcmd != "WHOISNOTICE") + // 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") { - Version ver = thiscmd->creator->GetVersion(); - if (ver.Flags & VF_OPTCOMMON) + std::string::size_type colon = line.find(':', b); + if (colon != std::string::npos) { - ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Removing ENCAP on '%s' for 1201-protocol server", - subcmd.c_str()); - line.erase(a, c-a); + for (std::string::iterator i = line.begin()+colon; i != line.end(); ++i) + { + if (*i == ' ') + *i = '_'; + } + line.erase(colon, 1); } } } + ServerInstance->Logs->Log(MODNAME, LOG_RAWIO, "S[%d] O %s", this->GetFd(), line.c_str()); + this->WriteData(line); + this->WriteData(newline); + return; + } + } + + ServerInstance->Logs->Log(MODNAME, LOG_RAWIO, "S[%d] O %s", this->GetFd(), original_line.c_str()); + this->WriteData(original_line); + this->WriteData(newline); +} + +namespace +{ + bool InsertCurrentChannelTS(std::vector<std::string>& params) + { + Channel* chan = ServerInstance->FindChan(params[0]); + if (!chan) + return false; + + // Insert the current TS of the channel between the first and the second parameters + params.insert(params.begin()+1, 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; + } - 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); + return true; // Passthru } diff --git a/src/modules/m_spanningtree/delline.cpp b/src/modules/m_spanningtree/delline.cpp index 540ca5079..c76af2fb7 100644 --- a/src/modules/m_spanningtree/delline.cpp +++ b/src/modules/m_spanningtree/delline.cpp @@ -20,38 +20,18 @@ #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; } - diff --git a/src/modules/m_spanningtree/encap.cpp b/src/modules/m_spanningtree/encap.cpp index dabfc086b..566f15da8 100644 --- a/src/modules/m_spanningtree/encap.cpp +++ b/src/modules/m_spanningtree/encap.cpp @@ -18,32 +18,28 @@ #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]); +} diff --git a/src/modules/m_spanningtree/fjoin.cpp b/src/modules/m_spanningtree/fjoin.cpp index ee18c8e87..ea7711332 100644 --- a/src/modules/m_spanningtree/fjoin.cpp +++ b/src/modules/m_spanningtree/fjoin.cpp @@ -26,10 +26,9 @@ #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 @@ -54,71 +53,67 @@ CmdResult CommandFJoin::Handle(const std::vector<std::string>& params, User *src * 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> :[[modes,]<uuid> [[modes,]<uuid> ... ]] + * The last parameter is a list consisting of zero or more (modelist, uuid) + * pairs (permanent channels may have zero users). The mode list for each + * user 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. + * */ - 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 */ - - TreeSocket* src_socket = Utils->FindServer(srcuser->server)->GetRoute()->GetSocket(); - + time_t TS = ConvToInt(params[1]); 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()); + ServerInstance->Logs->Log(MODNAME, LOG_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->GetName().c_str()); return CMD_INVALID; } - if (created) + const std::string& channel = params[0]; + Channel* chan = ServerInstance->FindChan(channel); + bool apply_other_sides_modes = true; + + 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 */ if (apply_other_sides_modes) { // Need to use a modestacker here due to maxmodes @@ -148,109 +143,128 @@ CmdResult CommandFJoin::Handle(const std::vector<std::string>& params, User *src while (stack.GetStackedLine(modelist)) { - ServerInstance->Modes->Process(modelist, srcuser, true); + ServerInstance->Modes->Process(modelist, srcuser, ModeParser::MODE_LOCALONLY | ModeParser::MODE_MERGE); modelist.erase(modelist.begin() + 1, modelist.end()); } - - ServerInstance->Modes->Process(modelist, srcuser, true); } - /* Now, process every 'modes,nick' pair */ + irc::modestacker modestack(true); + TreeSocket* src_socket = TreeServer::Get(srcuser)->GetSocket(); + + /* Now, process every 'modes,uuid' pair */ + irc::tokenstream users(*params.rbegin()); + std::string item; + irc::modestacker* modestackptr = (apply_other_sides_modes ? &modestack : NULL); while (users.GetToken(item)) { - const char* usr = item.c_str(); - if (usr && *usr) - { - const char* unparsedmodes = usr; - std::string modes; - + if (!ProcessModeUUIDPair(item, src_socket, chan, modestackptr)) + return CMD_INVALID; + } - /* 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; - } + /* Flush mode stacker if we lost the FJOIN or had equal TS */ + if (apply_other_sides_modes) + CommandFJoin::ApplyModeStack(srcuser, chan, modestack); - modes += *unparsedmodes; - usr++; - unparsedmodes++; - } + return CMD_SUCCESS; +} - /* Advance past the comma, to the nick */ - usr++; +bool CommandFJoin::ProcessModeUUIDPair(const std::string& item, TreeSocket* src_socket, Channel* chan, irc::modestacker* modestack) +{ + std::string::size_type comma = item.find(','); - /* 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; + // Comma not required anymore if the user has no modes + std::string uuid = ((comma == std::string::npos) ? item : item.substr(comma+1)); + User* who = ServerInstance->FindUUID(uuid); + if (!who) + { + // Probably KILLed, ignore + return true; + } - /* 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); + /* Check that the user's 'direction' is correct */ + TreeServer* route_back_again = TreeServer::Get(who); + if (route_back_again->GetSocket() != src_socket) + { + return true; + } - Channel::JoinUser(who, channel.c_str(), true, "", route_back_again->bursting, TS); - } - else + /* Check if the user received at least one mode */ + if ((modestack) && (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) + { + if (!ServerInstance->Modes->FindMode(*i, MODETYPE_CHANNEL)) { - ServerInstance->Logs->Log("m_spanningtree",SPARSE, "Ignored nonexistant user %s in fjoin to %s (probably quit?)", usr, channel.c_str()); - continue; + ServerInstance->SNO->WriteToSnoMask('d', "Unrecognised mode '%c' for a user in FJOIN, dropping link", *i); + return false; } + + /* Add any modes this user had to the mode stack */ + modestack->Push(*i, who->nick); } } - /* Flush mode stacker if we lost the FJOIN or had equal TS */ - if (apply_other_sides_modes) + chan->ForceJoin(who, NULL, route_back_again->bursting); + return true; +} + +void CommandFJoin::RemoveStatus(Channel* c) +{ + irc::modestacker stack(false); + + 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, stack); } - return CMD_SUCCESS; + + ApplyModeStack(ServerInstance->FakeClient, c, stack); } -void CommandFJoin::RemoveStatus(User* srcuser, parameterlist ¶ms) +void CommandFJoin::ApplyModeStack(User* srcuser, Channel* c, irc::modestacker& stack) { - if (params.size() < 1) - return; + parameterlist stackresult; + stackresult.push_back(c->name); - Channel* c = ServerInstance->FindChan(params[0]); - - if (c) + while (stack.GetStackedLine(stackresult)) { - irc::modestacker stack(false); - parameterlist stackresult; - stackresult.push_back(c->name); + ServerInstance->Modes->Process(stackresult, srcuser, ModeParser::MODE_LOCALONLY); + stackresult.erase(stackresult.begin() + 1, stackresult.end()); + } +} - 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); - } +void CommandFJoin::LowerTS(Channel* chan, time_t TS, const std::string& newname) +{ + 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); - while (stack.GetStackedLine(stackresult)) - { - ServerInstance->SendMode(stackresult, srcuser); - stackresult.erase(stackresult.begin() + 1, stackresult.end()); - } + // While the name is equal in case-insensitive compare, it might differ in case; use the remote version + chan->name = newname; + chan->age = TS; + + // Remove all pending invites + chan->ClearInvites(); + + // Clear all modes + CommandFJoin::RemoveStatus(chan); + + // 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; } - diff --git a/src/modules/m_spanningtree/fmode.cpp b/src/modules/m_spanningtree/fmode.cpp index c1e452db6..48e4a19b9 100644 --- a/src/modules/m_spanningtree/fmode.cpp +++ b/src/modules/m_spanningtree/fmode.cpp @@ -21,73 +21,57 @@ #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++) + time_t TS = ConvToInt(params[1]); + if (!TS) { - 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]); - } - + ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "*** BUG? *** TS of 0 sent to FMODE. Are some services authors smoking craq, or is it 1970 again?. Dropping link."); + ServerInstance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending FMODE with a TS of zero. Total craq, dropping link.", who->server->GetName().c_str()); + return CMD_INVALID; } - /* Extract the TS value of the object, either User or Channel */ - User* dst = ServerInstance->FindNick(params[0]); - Channel* chan = NULL; - time_t ourTS = 0; - if (dst) + /* Extract the TS value of the object, either User or Channel */ + time_t ourTS; + if (params[0][0] == '#') { - ourTS = dst->age; + Channel* chan = ServerInstance->FindChan(params[0]); + if (!chan) + /* Oops, channel doesn't exist! */ + return CMD_FAILURE; + + ourTS = chan->age; } else { - chan = ServerInstance->FindChan(params[0]); - if (chan) - { - ourTS = chan->age; - } - else - /* Oops, channel doesnt exist! */ + User* user = ServerInstance->FindUUID(params[0]); + if (!user) return CMD_FAILURE; - } - 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 (IS_SERVER(user)) + return CMD_INVALID; + + ourTS = user->age; } - /* TS is equal or less: Merge the mode changes into ours and pass on. + /* If the TS is greater than ours, we drop the mode and don't pass it anywhere. */ - 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. + if (TS > ourTS) + return CMD_FAILURE; + + /* TS is equal or less: Merge the mode changes into ours and pass on. */ - return CMD_FAILURE; -} + std::vector<std::string> modelist; + modelist.reserve(params.size()-1); + /* Insert everything into modelist except the TS (params[1]) */ + modelist.push_back(params[0]); + modelist.insert(modelist.end(), params.begin()+2, params.end()); + ModeParser::ModeProcessFlag flags = ModeParser::MODE_LOCALONLY; + if ((TS == ourTS) && IS_SERVER(who)) + flags |= ModeParser::MODE_MERGE; + ServerInstance->Modes->Process(modelist, who, flags); + return CMD_SUCCESS; +} diff --git a/src/modules/m_spanningtree/ftopic.cpp b/src/modules/m_spanningtree/ftopic.cpp index d559c6ae5..bd15489a2 100644 --- a/src/modules/m_spanningtree/ftopic.cpp +++ b/src/modules/m_spanningtree/ftopic.cpp @@ -21,31 +21,88 @@ #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; + + time_t ChanTS = ConvToInt(params[1]); + if (!ChanTS) + return CMD_INVALID; + + if (c->age < ChanTS) + // Our channel TS is older, nothing to do + return CMD_FAILURE; + + time_t ts = ConvToInt(params[2]); + if (!ts) + return CMD_INVALID; + + // Channel::topicset is initialized to 0 on channel creation, so their ts will always win if we never had a topic + 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); +} diff --git a/src/modules/m_spanningtree/hmac.cpp b/src/modules/m_spanningtree/hmac.cpp index d990e1fbf..d79f13567 100644 --- a/src/modules/m_spanningtree/hmac.cpp +++ b/src/modules/m_spanningtree/hmac.cpp @@ -19,18 +19,12 @@ #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() { @@ -62,39 +56,10 @@ std::string TreeSocket::MakePass(const std::string &password, const std::string */ 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; + return "AUTH:" + BinToBase64(sha256->hmac(password, challenge)); - 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 (!challenge.empty() && !sha256) + ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Not authenticating to server using SHA256/HMAC because we don't have m_sha256 loaded!"); return password; } @@ -104,16 +69,6 @@ bool TreeSocket::ComparePass(const Link& link, const std::string &theirs) capab->auth_fingerprint = !link.Fingerprint.empty(); capab->auth_challenge = !capab->ourchallenge.empty() && !capab->theirchallenge.empty(); - std::string fp; - if (GetIOHook()) - { - SocketCertificateRequest req(this, Utils->Creator); - if (req.cert) - { - fp = req.cert->GetFingerprint(); - } - } - if (capab->auth_challenge) { std::string our_hmac = MakePass(link.RecvPass, capab->ourchallenge); @@ -129,6 +84,7 @@ bool TreeSocket::ComparePass(const Link& link, const std::string &theirs) return false; } + std::string fp = SSLClientCert::GetFingerprint(this); if (capab->auth_fingerprint) { /* Require fingerprint to exist and match */ diff --git a/src/modules/m_spanningtree/idle.cpp b/src/modules/m_spanningtree/idle.cpp index 0ea06a3cc..1b020701b 100644 --- a/src/modules/m_spanningtree/idle.cpp +++ b/src/modules/m_spanningtree/idle.cpp @@ -18,67 +18,53 @@ #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 = abs((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; +} diff --git a/src/modules/m_spanningtree/ijoin.cpp b/src/modules/m_spanningtree/ijoin.cpp new file mode 100644 index 000000000..a579848c8 --- /dev/null +++ b/src/modules/m_spanningtree/ijoin.cpp @@ -0,0 +1,84 @@ +/* + * 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() > 1) + { + time_t RemoteTS = ConvToInt(params[1]); + if (!RemoteTS) + { + ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Invalid TS in IJOIN: " + params[1]); + return CMD_INVALID; + } + + if (RemoteTS < chan->age) + { + ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Attempted to lower TS via IJOIN. Channel=" + params[0] + " RemoteTS=" + params[1] + " LocalTS=" + ConvToStr(chan->age)); + return CMD_INVALID; + } + apply_modes = ((params.size() > 2) && (RemoteTS == chan->age)); + } + else + apply_modes = false; + + chan->ForceJoin(user, apply_modes ? ¶ms[2] : NULL); + 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()) + { + ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Received RESYNC with a source that is not directly connected: " + server->GetID()); + return CMD_INVALID; + } + + // Send all known information about the channel + server->GetSocket()->SyncChannel(chan); + return CMD_SUCCESS; +} diff --git a/src/modules/m_spanningtree/link.h b/src/modules/m_spanningtree/link.h index 797f108d8..21213fb3e 100644 --- a/src/modules/m_spanningtree/link.h +++ b/src/modules/m_spanningtree/link.h @@ -18,8 +18,7 @@ */ -#ifndef M_SPANNINGTREE_LINK_H -#define M_SPANNINGTREE_LINK_H +#pragma once class Link : public refcountbase { @@ -31,7 +30,7 @@ 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; @@ -51,5 +50,3 @@ class Autoconnect : public refcountbase int position; Autoconnect(ConfigTag* Tag) : tag(Tag) {} }; - -#endif diff --git a/src/modules/m_spanningtree/main.cpp b/src/modules/m_spanningtree/main.cpp index d702ab4a2..0dc680ca0 100644 --- a/src/modules/m_spanningtree/main.cpp +++ b/src/modules/m_spanningtree/main.cpp @@ -21,13 +21,11 @@ */ -/* $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" @@ -38,57 +36,52 @@ #include "protocolinterface.h" ModuleSpanningTree::ModuleSpanningTree() - : KeepNickTS(false) + : rconnect(this), rsquit(this), map(this) + , commands(NULL), DNS(this, "DNS") { - 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), version(module), + burst(module), endburst(module) +{ +} + +namespace { + void SetLocalUsersServer(Server* newserver) + { + ServerInstance->FakeClient->server = newserver; + const LocalUserList& list = ServerInstance->Users->local_users; + for (LocalUserList::const_iterator i = list.begin(); i != list.end(); ++i) + (*i)->server = newserver; + } } 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)); + ServerInstance->SNO->EnableSnomask('l', "LINK"); + + Utils = new SpanningTreeUtilities(this); + Utils->TreeRoot = new TreeServer; + commands = new SpanningTreeCommands(this); delete ServerInstance->PI; - ServerInstance->PI = new SpanningTreeProtocolInterface(Utils); + ServerInstance->PI = new SpanningTreeProtocolInterface; + + delete ServerInstance->FakeClient->server; + SetLocalUsersServer(Utils->TreeRoot); + loopCall = false; + SplitInProgress = false; // update our local user count - Utils->TreeRoot->SetUserCount(ServerInstance->Users->local_users.size()); + Utils->TreeRoot->UserCount = ServerInstance->Users->local_users.size(); } void ModuleSpanningTree::ShowLinks(TreeServer* Current, User* user, int hops) @@ -98,44 +91,40 @@ 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) @@ -165,62 +154,53 @@ restart: { TreeServer *s = i->second; - if (s->GetSocket() && s->GetSocket()->GetLinkState() == DYING) + // Skip myself + if (s->IsRoot()) + continue; + + if (s->GetSocket()->GetLinkState() == DYING) { s->GetSocket()->Close(); goto restart; } - // 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(":" + ServerInstance->Config->GetSID() + " PING " + 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; } } } @@ -272,7 +252,7 @@ void ModuleSpanningTree::ConnectServer(Link* x, Autoconnect* y) return; } - QueryType start_type = DNS_QUERY_AAAA; + DNS::QueryType start_type = DNS::QUERY_AAAA; if (strchr(x->IPAddr.c_str(),':')) { in6_addr n; @@ -290,7 +270,7 @@ void ModuleSpanningTree::ConnectServer(Link* x, Autoconnect* y) 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 */ @@ -302,17 +282,21 @@ void ModuleSpanningTree::ConnectServer(Link* x, Autoconnect* y) 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); } } @@ -360,16 +344,17 @@ ModResult ModuleSpanningTree::HandleVersion(const std::vector<std::string>& para 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; } + std::string Version = 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; } @@ -378,15 +363,11 @@ ModResult ModuleSpanningTree::HandleVersion(const std::vector<std::string>& para */ 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); } @@ -413,8 +394,7 @@ ModResult ModuleSpanningTree::HandleConnect(const std::vector<std::string>& para } 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; } } @@ -423,24 +403,20 @@ ModResult ModuleSpanningTree::HandleConnect(const std::vector<std::string>& para return MOD_RES_DENY; } -void ModuleSpanningTree::OnGetServerDescription(const std::string &servername,std::string &description) +void ModuleSpanningTree::On005Numeric(std::map<std::string, std::string>& tokens) { - TreeServer* s = Utils->FindServer(servername); - if (s) - { - description = s->GetDesc(); - } + tokens["MAP"]; } 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_back(ConvToStr(expiry)); - Utils->DoOneToMany(source->uuid,"INVITE",params); + params.Broadcast(); } } @@ -450,123 +426,37 @@ void ModuleSpanningTree::OnPostTopicChange(User* user, Channel* chan, const std: if (!IS_LOCAL(user)) return; - parameterlist params; - params.push_back(chan->name); - params.push_back(":"+topic); - Utils->DoOneToMany(user->uuid,"TOPIC",params); -} - -void ModuleSpanningTree::OnWallops(User* user, const std::string &text) -{ - 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); - } - } + CommandFTopic::Builder(user, chan).Broadcast(); } -void ModuleSpanningTree::OnUserMessage(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list) +void ModuleSpanningTree::OnUserMessage(User* user, void* dest, int target_type, const std::string& text, char status, const CUList& exempt_list, MessageType msgtype) { - /* 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(); } } @@ -582,25 +472,10 @@ void ModuleSpanningTree::OnUserConnect(LocalUser* user) 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++) { @@ -610,23 +485,34 @@ void ModuleSpanningTree::OnUserConnect(LocalUser* user) 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; + + 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. + CmdBuilder params("FJOIN"); 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_raw(" +").push_raw(memb->chan->ChanModes(true)); + params.push(memb->modes).push_raw(',').push_raw(memb->user->uuid); + params.Broadcast(); + } + else + { + CmdBuilder params(memb->user, "IJOIN"); + params.push_back(memb->chan->name); + if (!memb->modes.empty()) + { + params.push_back(ConvToStr(memb->chan->age)); + params.push_back(memb->modes); + } + params.Broadcast(); } } @@ -635,9 +521,7 @@ void ModuleSpanningTree::OnChangeHost(User* user, const std::string &newhost) 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) @@ -645,9 +529,7 @@ 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) @@ -655,101 +537,76 @@ 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 + bool hide = (((this->SplitInProgress) && (Utils->quiet_bursts)) || (user->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) { - parameterlist params; + CmdBuilder params("SAVE"); params.push_back(user->uuid); params.push_back(ConvToStr(user->age)); - Utils->DoOneToMany(ServerInstance->Config->GetSID(),"SAVE",params); + params.Broadcast(); } } 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); + params.push_last(reason); + params.Broadcast(); } void ModuleSpanningTree::OnPreRehash(User* user, const std::string ¶meter) @@ -757,18 +614,18 @@ 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) { // Re-read config stuff try @@ -783,8 +640,8 @@ void ModuleSpanningTree::OnRehash(User* user) 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); } } @@ -799,19 +656,21 @@ void ModuleSpanningTree::OnLoadModule(Module* mod) 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); - 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(); @@ -821,7 +680,7 @@ void ModuleSpanningTree::OnUnloadModule(Module* mod) 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(); } } @@ -833,139 +692,46 @@ void ModuleSpanningTree::OnOper(User* user, const std::string &opertype) { 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::ProtoSendMode(void* opaque, TargetTypeFlags target_type, void* target, const parameterlist &modeline, const std::vector<TranslateType> &translate) -{ - TreeSocket* s = (TreeSocket*)opaque; - std::string output_text; - - ServerInstance->Parser->TranslateUIDs(translate, modeline, output_text); - - if (target) - { - 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); - } - } -} - -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(); } @@ -974,6 +740,9 @@ ModuleSpanningTree::~ModuleSpanningTree() delete ServerInstance->PI; ServerInstance->PI = new ProtocolInterface; + Server* newsrv = new Server(ServerInstance->Config->ServerName, ServerInstance->Config->ServerDesc); + SetLocalUsersServer(newsrv); + /* This will also free the listeners */ delete Utils; diff --git a/src/modules/m_spanningtree/main.h b/src/modules/m_spanningtree/main.h index eb17c4195..513e86a2f 100644 --- a/src/modules/m_spanningtree/main.h +++ b/src/modules/m_spanningtree/main.h @@ -21,11 +21,12 @@ */ -#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" /** If you make a change which breaks the protocol, increment this. * If you completely change the protocol, completely change the number. @@ -36,8 +37,8 @@ * 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 */ @@ -52,47 +53,43 @@ class Autoconnect; */ 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; 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. + /** True if users are quitting due to a netsplit */ - bool KeepNickTS; + bool SplitInProgress; /** 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); @@ -133,55 +130,43 @@ class ModuleSpanningTree : public Module */ 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); + 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 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 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 On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE; CullResult cull(); ~ModuleSpanningTree(); - Version GetVersion(); + Version GetVersion() CXX11_OVERRIDE; void Prioritize(); }; - -#endif diff --git a/src/modules/m_spanningtree/metadata.cpp b/src/modules/m_spanningtree/metadata.cpp index a584f8fa8..5dea7ffae 100644 --- a/src/modules/m_spanningtree/metadata.cpp +++ b/src/modules/m_spanningtree/metadata.cpp @@ -21,39 +21,79 @@ #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) + return CMD_INVALID; + 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 = ConvToInt(params[1]); + if (!ChanTS) + return CMD_INVALID; + + 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); +} diff --git a/src/modules/m_spanningtree/misccommands.cpp b/src/modules/m_spanningtree/misccommands.cpp new file mode 100644 index 000000000..5b04c73bc --- /dev/null +++ b/src/modules/m_spanningtree/misccommands.cpp @@ -0,0 +1,48 @@ +/* + * 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 CommandBurst::HandleServer(TreeServer* server, std::vector<std::string>& params) +{ + server->bursting = true; + return CMD_SUCCESS; +} + +CmdResult CommandEndBurst::HandleServer(TreeServer* server, std::vector<std::string>& params) +{ + server->FinishBurst(); + return CMD_SUCCESS; +} diff --git a/src/modules/m_spanningtree/netburst.cpp b/src/modules/m_spanningtree/netburst.cpp index d508c092d..a33cf8a13 100644 --- a/src/modules/m_spanningtree/netburst.cpp +++ b/src/modules/m_spanningtree/netburst.cpp @@ -21,11 +21,80 @@ #include "inspircd.h" #include "xline.h" +#include "listmode.h" #include "treesocket.h" #include "treeserver.h" -#include "utils.h" #include "main.h" +#include "commands.h" +#include "protocolinterface.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 @@ -34,9 +103,8 @@ */ 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(), + s->GetName().c_str(), capab->auth_fingerprint ? "SSL Fingerprint and " : "", capab->auth_challenge ? "challenge-response" : "plaintext password"); this->CleanNegotiationInfo(); @@ -44,42 +112,41 @@ void TreeSocket::DoBurst(TreeServer* s) /* send our version string */ this->WriteLine(":" + ServerInstance->Config->GetSID() + " VERSION :"+ServerInstance->GetVersionString()); /* 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)); + FOREACH_MOD(OnSyncNetwork, (bs.server)); this->WriteLine(":" + ServerInstance->Config->GetSID() + " ENDBURST"); ServerInstance->SNO->WriteToSnoMask('l',"Finished bursting to \2"+ s->GetName()+"\2."); } -/** Recursively send the server tree with distances as hops. +/** 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. + * The hopcount parameter (3rd) is deprecated, and is always 0. */ -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++) + 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)); + this->WriteLine(":" + recursive_server->GetID() + " VERSION :" + recursive_server->GetVersion()); /* down to next level */ - this->SendServers(recursive_server, s, hops+1); + this->SendServers(recursive_server, s); } } } @@ -87,101 +154,43 @@ void TreeSocket::SendServers(TreeServer* Current, TreeServer* s, int hops) /** 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; + std::string line(":"); + line.append(ServerInstance->Config->GetSID()).append(" FJOIN ").append(c->name).append(1, ' ').append(ConvToStr(c->age)).append(" +"); + std::string::size_type erase_from = line.length(); + line.append(c->ChanModes(true)).append(" :"); const UserMembList *ulist = c->GetUsers(); - std::string modes; - std::string params; - for (UserMembCIter i = ulist->begin(); i != ulist->end(); i++) + 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) + const std::string& modestr = i->second->modes; + if ((line.length() + modestr.length() + UIDGenerator::UUID_LENGTH + 2) > 480) { - // remove the final space - if (ptr[-1] == ' ') - ptr[-1] = '\0'; - buffer.append(list).append("\r\n"); - curlen = headlen; - ptr = list + headlen; - numusers = 0; + this->WriteLine(line); + line.erase(erase_from); + line.append(" :"); } - - 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"); + line.append(modestr).append(1, ',').append(i->first->uuid).push_back(' '); } - - int linesize = 1; - for (BanList::iterator b = c->bans.begin(); b != c->bans.end(); b++) - { - 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)) - { - /* 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; - } - } - - /* 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(line); } /** 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) @@ -192,96 +201,101 @@ void TreeSocket::SendXLines() 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()); +} + +/** Send channel topic, modes and metadata */ +void TreeSocket::SyncChannel(Channel* chan, BurstState& bs) +{ + SendFJoins(chan); - FOREACH_MOD(I_OnSyncChannel,OnSyncChannel(c->second,Utils->Creator,this)); + // 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)); } } - diff --git a/src/modules/m_spanningtree/nick.cpp b/src/modules/m_spanningtree/nick.cpp new file mode 100644 index 000000000..49ce9a767 --- /dev/null +++ b/src/modules/m_spanningtree/nick.cpp @@ -0,0 +1,62 @@ +/* + * 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)) + return CMD_INVALID; + + /* Update timestamp on user when they change nicks */ + const time_t newts = ConvToInt(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 is local, who is remote */ + int collideret = Utils->DoCollision(x, TreeServer::Get(user), newts, user->ident, user->GetIPString(), user->uuid); + if (collideret != 1) + { + /* + * Remote client lost, or both lost, parsing or passing on this + * nickchange would be pointless, as the incoming client's server will + * soon receive SAVE to change its nick to its UID. :) -- w00t + */ + return CMD_FAILURE; + } + } + user->ForceNickChange(params[0], newts); + return CMD_SUCCESS; +} diff --git a/src/modules/m_spanningtree/nickcollide.cpp b/src/modules/m_spanningtree/nickcollide.cpp index 38d59affb..62e43a0b1 100644 --- a/src/modules/m_spanningtree/nickcollide.cpp +++ b/src/modules/m_spanningtree/nickcollide.cpp @@ -19,14 +19,11 @@ #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" /* * Yes, this function looks a little ugly. @@ -34,7 +31,7 @@ * Returns 1 if colliding local client, 2 if colliding remote, 3 if colliding both. * Sends SAVEs as appropriate and forces nickchanges too. */ -int TreeSocket::DoCollision(User *u, time_t remotets, const std::string &remoteident, const std::string &remoteip, const std::string &remoteuid) +int SpanningTreeUtilities::DoCollision(User* u, TreeServer* server, time_t remotets, const std::string& remoteident, const std::string& remoteip, const std::string& remoteuid) { /* * Under old protocol rules, we would have had to kill both clients. @@ -109,12 +106,12 @@ int TreeSocket::DoCollision(User *u, time_t remotets, const std::string &remotei * Local-side nick needs to change. Just in case we are hub, and * this "local" nick is actually behind us, send an 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()); + u->ForceNickChange(u->uuid); if (!bChangeRemote) return 1; @@ -127,12 +124,13 @@ int TreeSocket::DoCollision(User *u, time_t remotets, const std::string &remotei * the UID or halt the propagation of the nick change command, * so other servers don't need to see the SAVE */ - WriteLine(":"+ServerInstance->Config->GetSID()+" SAVE "+remoteuid+" "+ ConvToStr(remotets)); + TreeSocket* sock = server->GetSocket(); + sock->WriteLine(":"+ServerInstance->Config->GetSID()+" SAVE "+remoteuid+" "+ ConvToStr(remotets)); if (remote) { /* nick change collide. Force change their nick. */ - remote->ForceNickChange(remoteuid.c_str()); + remote->ForceNickChange(remoteuid); } if (!bChangeLocal) @@ -141,4 +139,3 @@ int TreeSocket::DoCollision(User *u, time_t remotets, const std::string &remotei return 3; } - diff --git a/src/modules/m_spanningtree/opertype.cpp b/src/modules/m_spanningtree/opertype.cpp index 97a4de8c2..1a9e36f72 100644 --- a/src/modules/m_spanningtree/opertype.cpp +++ b/src/modules/m_spanningtree/opertype.cpp @@ -26,15 +26,17 @@ /** 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); + + OperIndex::iterator iter = ServerInstance->Config->OperTypes.find(opertype); + if (iter != ServerInstance->Config->OperTypes.end()) u->oper = iter->second; else { @@ -48,12 +50,17 @@ CmdResult CommandOpertype::Handle(const std::vector<std::string>& params, User * * 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->bursting || 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); +} diff --git a/src/modules/m_spanningtree/override_map.cpp b/src/modules/m_spanningtree/override_map.cpp index 04fa4bcab..68551e84f 100644 --- a/src/modules/m_spanningtree/override_map.cpp +++ b/src/modules/m_spanningtree/override_map.cpp @@ -1,6 +1,7 @@ /* * 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> @@ -19,178 +20,203 @@ */ -/* $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; +} diff --git a/src/modules/m_spanningtree/override_squit.cpp b/src/modules/m_spanningtree/override_squit.cpp index 7d01c8149..84cb01f50 100644 --- a/src/modules/m_spanningtree/override_squit.cpp +++ b/src/modules/m_spanningtree/override_squit.cpp @@ -17,48 +17,41 @@ */ -/* $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(); } 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; } - diff --git a/src/modules/m_spanningtree/override_stats.cpp b/src/modules/m_spanningtree/override_stats.cpp index 688661b80..14b3f5ef7 100644 --- a/src/modules/m_spanningtree/override_stats.cpp +++ b/src/modules/m_spanningtree/override_stats.cpp @@ -18,16 +18,11 @@ */ -/* $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) { @@ -36,12 +31,22 @@ ModResult ModuleSpanningTree::OnStats(char statschar, User* user, string_list &r 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; } - diff --git a/src/modules/m_spanningtree/override_whois.cpp b/src/modules/m_spanningtree/override_whois.cpp index ad8c6a6ef..d7030e30a 100644 --- a/src/modules/m_spanningtree/override_whois.cpp +++ b/src/modules/m_spanningtree/override_whois.cpp @@ -16,19 +16,10 @@ * 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) { @@ -37,18 +28,15 @@ ModResult ModuleSpanningTree::HandleRemoteWhois(const std::vector<std::string>& 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); + CmdBuilder(user, "IDLE").push(remote->uuid).Unicast(remote); 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()); + 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; } - diff --git a/src/modules/m_spanningtree/ping.cpp b/src/modules/m_spanningtree/ping.cpp index aec680b23..878f8af3a 100644 --- a/src/modules/m_spanningtree/ping.cpp +++ b/src/modules/m_spanningtree/ping.cpp @@ -18,44 +18,24 @@ #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; } - - diff --git a/src/modules/m_spanningtree/pong.cpp b/src/modules/m_spanningtree/pong.cpp index 5966d05d9..6a29163e4 100644 --- a/src/modules/m_spanningtree/pong.cpp +++ b/src/modules/m_spanningtree/pong.cpp @@ -18,65 +18,26 @@ #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->bursting) { - 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; } - diff --git a/src/modules/m_spanningtree/postcommand.cpp b/src/modules/m_spanningtree/postcommand.cpp index 471bbfcb9..9f90f3458 100644 --- a/src/modules/m_spanningtree/postcommand.cpp +++ b/src/modules/m_spanningtree/postcommand.cpp @@ -17,69 +17,45 @@ * 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()); - 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) { TreeServer* sdest = FindServer(routing.serverdest); if (!sdest) { - ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"Trying to route ENCAP to nonexistant server %s", + ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Trying to route ENCAP to nonexistant server %s", routing.serverdest.c_str()); return; } params.push_back(sdest->GetID()); params.push_back(command); - sent_cmd = "ENCAP"; } else { @@ -88,14 +64,13 @@ void SpanningTreeUtilities::RouteCommand(TreeServer* origin, const std::string & 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); @@ -113,52 +88,35 @@ void SpanningTreeUtilities::RouteCommand(TreeServer* origin, const std::string & 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(routing.serverdest); } } diff --git a/src/modules/m_spanningtree/precommand.cpp b/src/modules/m_spanningtree/precommand.cpp index b331571ca..4733d0071 100644 --- a/src/modules/m_spanningtree/precommand.cpp +++ b/src/modules/m_spanningtree/precommand.cpp @@ -18,18 +18,9 @@ */ -/* $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) { @@ -45,10 +36,6 @@ ModResult ModuleSpanningTree::OnPreCommand(std::string &command, std::vector<std { 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); @@ -64,9 +51,7 @@ ModResult ModuleSpanningTree::OnPreCommand(std::string &command, std::vector<std } else if ((command == "VERSION") && (parameters.size() > 0)) { - this->HandleVersion(parameters,user); - return MOD_RES_DENY; + return this->HandleVersion(parameters,user); } return MOD_RES_PASSTHRU; } - diff --git a/src/modules/m_spanningtree/protocolinterface.cpp b/src/modules/m_spanningtree/protocolinterface.cpp index 3ab5dae9d..192f7cff2 100644 --- a/src/modules/m_spanningtree/protocolinterface.cpp +++ b/src/modules/m_spanningtree/protocolinterface.cpp @@ -19,161 +19,137 @@ #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) - { - Utils->DoOneToMany(ServerInstance->Config->GetSID(), "ENCAP", encap); - return true; - } - return Utils->DoOneToOne(ServerInstance->Config->GetSID(), "ENCAP", encap, encap[0]); -} + if (!source) + source = ServerInstance->FakeClient; -void SpanningTreeProtocolInterface::SendMetaData(Extensible* target, const std::string &key, const std::string &data) -{ - parameterlist params; + CmdBuilder encap(source, "ENCAP"); - 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); + // Are there any wildcards in the target string? + if (targetmask.find_first_of("*?") != std::string::npos) + { + // Yes, send the target string as-is; servers will decide whether or not it matches them + encap.push(targetmask).push(cmd).insert(params).Broadcast(); + } 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; - - params.push_back(channel->name); - params.push_back(ConvToStr(ServerInstance->Time())); - params.push_back(ServerInstance->Config->ServerName); - params.push_back(":" + topic); + if (!source) + source = ServerInstance->FakeClient; - 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); + CommandMetadata::Builder(u, key, data).Broadcast(); +} - parameterlist outlist; - outlist.push_back(uidtarget); - outlist.push_back(outdata); +void SpanningTreeProtocolInterface::SendMetaData(Channel* c, const std::string& key, const std::string& data) +{ + CommandMetadata::Builder(c, key, data).Broadcast(); +} - 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); - } - } +void SpanningTreeProtocolInterface::SendMetaData(const std::string& key, const std::string& data) +{ + CommandMetadata::Builder(key, data).Broadcast(); } -void SpanningTreeProtocolInterface::SendSNONotice(const std::string &snomask, const std::string &text) +void SpanningTreeProtocolInterface::Server::SendMetaData(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); + sock->WriteLine(CommandMetadata::Builder(key, data)); } -void SpanningTreeProtocolInterface::PushToClient(User* target, const std::string &rawline) +void SpanningTreeProtocolInterface::SendTopic(Channel* channel, std::string &topic) { - parameterlist p; - p.push_back(target->uuid); - p.push_back(":" + rawline); - Utils->DoOneToOne(ServerInstance->Config->GetSID(), "PUSH", p, target->server); + CommandFTopic::Builder(ServerInstance->FakeClient, channel).Broadcast(); } -void SpanningTreeProtocolInterface::SendChannel(Channel* target, char status, const std::string &text) +void SpanningTreeProtocolInterface::SendMode(User* source, User* u, Channel* c, const std::vector<std::string>& modedata, const std::vector<TranslateType>& translate) { - 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++) + if (u) { - TreeSocket* Sock = i->second->GetSocket(); - if (Sock) - Sock->WriteLine(text); + if (u->registered != REG_ALL) + return; + + CmdBuilder params(source, "MODE"); + params.push_back(u->uuid); + params.insert(modedata); + params.Broadcast(); + } + else + { + CmdBuilder params(source, "FMODE"); + params.push_back(c->name); + params.push_back(ConvToStr(c->age)); + params.push_back(CommandParser::TranslateUIDs(translate, modedata)); + params.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); } diff --git a/src/modules/m_spanningtree/protocolinterface.h b/src/modules/m_spanningtree/protocolinterface.h index 297366893..97648f4b4 100644 --- a/src/modules/m_spanningtree/protocolinterface.h +++ b/src/modules/m_spanningtree/protocolinterface.h @@ -17,32 +17,30 @@ */ -#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 SendMode(User* source, User* usertarget, Channel* chantarget, const parameterlist& modedata, const std::vector<TranslateType>& types); + 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); +}; diff --git a/src/modules/m_spanningtree/push.cpp b/src/modules/m_spanningtree/push.cpp index b791376ea..b29b780c8 100644 --- a/src/modules/m_spanningtree/push.cpp +++ b/src/modules/m_spanningtree/push.cpp @@ -18,34 +18,18 @@ #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; } - diff --git a/src/modules/m_spanningtree/rconnect.cpp b/src/modules/m_spanningtree/rconnect.cpp index d4254cac6..c5d3a5b52 100644 --- a/src/modules/m_spanningtree/rconnect.cpp +++ b/src/modules/m_spanningtree/rconnect.cpp @@ -19,19 +19,13 @@ #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>"; @@ -39,14 +33,11 @@ CommandRConnect::CommandRConnect (Module* Creator, SpanningTreeUtilities* Util) 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? */ @@ -58,6 +49,21 @@ CmdResult CommandRConnect::Handle (const std::vector<std::string>& parameters, U 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; } diff --git a/src/modules/m_spanningtree/resolvers.cpp b/src/modules/m_spanningtree/resolvers.cpp index d7c4c5227..80e8aeb0e 100644 --- a/src/modules/m_spanningtree/resolvers.cpp +++ b/src/modules/m_spanningtree/resolvers.cpp @@ -19,9 +19,8 @@ #include "inspircd.h" -#include "socket.h" -#include "xline.h" +#include "cachetimer.h" #include "resolvers.h" #include "main.h" #include "utils.h" @@ -29,21 +28,22 @@ #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. @@ -51,7 +51,7 @@ void ServernameResolver::OnLookupComplete(const std::string &result, unsigned in 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 */ @@ -66,47 +66,74 @@ void ServernameResolver::OnLookupComplete(const std::string &result, unsigned in } } -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, ServerInstance->Time(), true) +{ +} + +bool CacheRefreshTimer::Tick(time_t TIME) +{ + Utils->RefreshIPCache(); + return true; } diff --git a/src/modules/m_spanningtree/resolvers.h b/src/modules/m_spanningtree/resolvers.h index 65b9e7249..782ac86ef 100644 --- a/src/modules/m_spanningtree/resolvers.h +++ b/src/modules/m_spanningtree/resolvers.h @@ -18,30 +18,27 @@ */ -#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. @@ -50,18 +47,15 @@ class SecurityIPResolver : public Resolver * 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 diff --git a/src/modules/m_spanningtree/rsquit.cpp b/src/modules/m_spanningtree/rsquit.cpp index 027ae02ab..988918c3f 100644 --- a/src/modules/m_spanningtree/rsquit.cpp +++ b/src/modules/m_spanningtree/rsquit.cpp @@ -19,17 +19,14 @@ #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]"; @@ -38,34 +35,28 @@ CommandRSQuit::CommandRSQuit (Module* Creator, SpanningTreeUtilities* Util) 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); + sock->Squit(server_target, "Server quit by " + user->GetFullRealHost() + " (" + reason + ")"); + sock->Close(); } return CMD_SUCCESS; @@ -75,20 +66,3 @@ RouteDescriptor CommandRSQuit::GetRouting(User* user, const std::vector<std::str { 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); - } -} - diff --git a/src/modules/m_spanningtree/save.cpp b/src/modules/m_spanningtree/save.cpp index 92999b422..03d401211 100644 --- a/src/modules/m_spanningtree/save.cpp +++ b/src/modules/m_spanningtree/save.cpp @@ -18,38 +18,29 @@ #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->FindNick(params[0]); + if ((!u) || (IS_SERVER(u))) + return CMD_FAILURE; + time_t ts = atol(params[1].c_str()); - if ((u) && (!IS_SERVER(u)) && (u->age == ts)) + if (u->age == ts) { - Utils->DoOneToAllButSender(prefix,"SAVE",params,prefix); - - if (!u->ForceNickChange(u->uuid.c_str())) + if (!u->ForceNickChange(u->uuid)) { ServerInstance->Users->QuitUser(u, "Nickname collision"); } } - return true; + return CMD_SUCCESS; } - diff --git a/src/modules/m_spanningtree/server.cpp b/src/modules/m_spanningtree/server.cpp index 05441da0c..69cae001c 100644 --- a/src/modules/m_spanningtree/server.cpp +++ b/src/modules/m_spanningtree/server.cpp @@ -19,72 +19,55 @@ #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); + 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); 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; } @@ -105,17 +88,9 @@ bool TreeSocket::Outbound_Reply_Server(parameterlist ¶ms) 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)) { this->SendError("Invalid format server ID: "+sid+"!"); @@ -163,15 +138,12 @@ bool TreeSocket::Outbound_Reply_Server(parameterlist ¶ms) Utils->timeoutlist.erase(this); linkID = sname; - MyRoot = new TreeServer(Utils, sname, description, sid, Utils->TreeRoot, this, x->Hidden); + MyRoot = new TreeServer(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); + // This will send a * in place of the password/hmac + CommandServer::Builder(MyRoot).Forward(MyRoot); return true; } @@ -194,7 +166,7 @@ bool TreeSocket::CheckDuplicate(const std::string& sname, const std::string& sid } /* 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) @@ -225,17 +197,9 @@ bool TreeSocket::Inbound_Server(parameterlist ¶ms) 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)) { this->SendError("Invalid format server ID: "+sid+"!"); @@ -281,3 +245,11 @@ bool TreeSocket::Inbound_Server(parameterlist ¶ms) return false; } +CommandServer::Builder::Builder(TreeServer* server) + : CmdBuilder(server->GetParent()->GetID(), "SERVER") +{ + push(server->GetName()); + push_raw(" * 0 "); + push_raw(server->GetID()); + push_last(server->GetDesc()); +} diff --git a/src/modules/m_spanningtree/servercommand.cpp b/src/modules/m_spanningtree/servercommand.cpp new file mode 100644 index 000000000..016788f51 --- /dev/null +++ b/src/modules/m_spanningtree/servercommand.cpp @@ -0,0 +1,49 @@ +/* + * 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; +} + +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; +} diff --git a/src/modules/m_spanningtree/servercommand.h b/src/modules/m_spanningtree/servercommand.h new file mode 100644 index 000000000..156b405e4 --- /dev/null +++ b/src/modules/m_spanningtree/servercommand.h @@ -0,0 +1,82 @@ +/* + * 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" + +/** 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); +}; + +/** 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) + return CMD_INVALID; + 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)) + return CMD_INVALID; + 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); +}; diff --git a/src/modules/m_spanningtree/svsjoin.cpp b/src/modules/m_spanningtree/svsjoin.cpp index 416502369..552e08dd3 100644 --- a/src/modules/m_spanningtree/svsjoin.cpp +++ b/src/modules/m_spanningtree/svsjoin.cpp @@ -19,19 +19,13 @@ #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 @@ -40,8 +34,9 @@ CmdResult CommandSVSJoin::Handle(const std::vector<std::string>& parameters, Use 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) + Channel::JoinUser(localuser, parameters[1]); return CMD_SUCCESS; } diff --git a/src/modules/m_spanningtree/svsnick.cpp b/src/modules/m_spanningtree/svsnick.cpp index 59973202d..43fa0f296 100644 --- a/src/modules/m_spanningtree/svsnick.cpp +++ b/src/modules/m_spanningtree/svsnick.cpp @@ -21,10 +21,9 @@ #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]); @@ -34,28 +33,18 @@ CmdResult CommandSVSNick::Handle(const std::vector<std::string>& parameters, Use 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->ForceNickChange(nick, NickTS)) { /* buh. UID them */ - if (!u->ForceNickChange(u->uuid.c_str())) + if (!u->ForceNickChange(u->uuid)) { ServerInstance->Users->QuitUser(u, "Nickname collision"); } } - - st->KeepNickTS = false; } return CMD_SUCCESS; diff --git a/src/modules/m_spanningtree/svspart.cpp b/src/modules/m_spanningtree/svspart.cpp index 3bdf13b25..f86afa367 100644 --- a/src/modules/m_spanningtree/svspart.cpp +++ b/src/modules/m_spanningtree/svspart.cpp @@ -19,16 +19,10 @@ #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) diff --git a/src/modules/m_spanningtree/treeserver.cpp b/src/modules/m_spanningtree/treeserver.cpp index 493b05ebf..3d57b1314 100644 --- a/src/modules/m_spanningtree/treeserver.cpp +++ b/src/modules/m_spanningtree/treeserver.cpp @@ -21,56 +21,42 @@ #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()), Socket(NULL), sid(ServerInstance->Config->GetSID()), ServerUser(ServerInstance->FakeClient) + , age(ServerInstance->Time()), Warned(false), bursting(false), UserCount(0), 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), ServerUser(new FakeUser(id, this)) + , age(ServerInstance->Time()), Warned(false), bursting(true), UserCount(0), OperCount(0), rtt(0), Hidden(Hide) { - age = ServerInstance->Time(); - bursting = true; - VersionString.clear(); - ServerUserCount = ServerOperCount = 0; + 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); + ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Server %s started bursting at time %lu", sid.c_str(), ts); /* find the 'route' for this server (e.g. the one directly connected * to the local server, which we can use to reach it) @@ -124,8 +110,6 @@ TreeServer::TreeServer(SpanningTreeUtilities* Util, std::string Name, std::strin */ this->AddHashEntry(); - - SetID(id); } const std::string& TreeServer::GetID() @@ -138,9 +122,9 @@ void TreeServer::FinishBurstInternal() this->bursting = false; SetNextPingTime(ServerInstance->Time() + Utils->PingFreq); SetPingFlag(); - for(unsigned int q=0; q < ChildCount(); q++) + for (ChildServers::const_iterator i = Children.begin(); i != Children.end(); ++i) { - TreeServer* child = GetChild(q); + TreeServer* child = *i; child->FinishBurstInternal(); } } @@ -152,43 +136,49 @@ void TreeServer::FinishBurst() 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()); -} - -void TreeServer::SetID(const std::string &id) -{ - ServerInstance->Logs->Log("m_spanningtree",DEBUG, "Setting SID to " + id); - sid = id; - Utils->sidlist[sid] = this; + GetName().c_str(), (bursttime > 10000 ? bursttime / 1000 : bursttime), (bursttime > 10000 ? "secs" : "msecs")); + AddServerEvent(Utils->Creator, GetName()); } int TreeServer::QuitUsers(const std::string &reason) { - 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++) + 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(); ) { - if (n->second->server == ServerName) - { - time_to_die.push_back(n->second); - } + User* user = i->second; + // Increment the iterator now because QuitUser() removes the user from the container + ++i; + if (user->server == this) + ServerInstance->Users->QuitUser(user, publicreason, &reason); } - for (std::vector<User*>::iterator n = time_to_die.begin(); n != time_to_die.end(); n++) + return original_size - users.size(); +} + +void TreeServer::CheckULine() +{ + uline = silentuline = false; + + ConfigTagList tags = ServerInstance->Config->ConfTags("uline"); + for (ConfigIter i = tags.first; i != tags.second; ++i) { - User* a = (User*)*n; - if (!IS_LOCAL(a)) + ConfigTag* tag = i->second; + std::string server = tag->getString("server"); + if (!strcasecmp(server.c_str(), GetName().c_str())) { - if (this->Utils->quiet_bursts) - a->quietquit = true; - - if (ServerInstance->Config->HideSplits) - ServerInstance->Users->QuitUser(a, "*.net *.split", reason_s); - else - ServerInstance->Users->QuitUser(a, reason_s); + 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 @@ -197,20 +187,8 @@ int TreeServer::QuitUsers(const std::string &reason) */ 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- @@ -221,16 +199,6 @@ TreeServer* TreeServer::GetRoute() return Route; } -std::string TreeServer::GetName() -{ - return ServerName.c_str(); -} - -const std::string& TreeServer::GetDesc() -{ - return ServerDesc; -} - const std::string& TreeServer::GetVersion() { return VersionString; @@ -257,26 +225,6 @@ void TreeServer::SetPingFlag() 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; @@ -292,28 +240,6 @@ void TreeServer::SetVersion(const std::string &Version) 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); @@ -334,13 +260,13 @@ bool TreeServer::DelChild(TreeServer* Child) * 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() +void TreeServer::Tidy() { while (1) { std::vector<TreeServer*>::iterator a = Children.begin(); if (a == Children.end()) - return true; + return; TreeServer* s = *a; s->Tidy(); s->cull(); @@ -351,7 +277,7 @@ bool TreeServer::Tidy() CullResult TreeServer::cull() { - if (ServerUser != ServerInstance->FakeClient) + if (!IsRoot()) ServerUser->cull(); return classbase::cull(); } @@ -359,11 +285,9 @@ CullResult TreeServer::cull() TreeServer::~TreeServer() { /* We'd better tidy up after ourselves, eh? */ - this->DelHashEntry(); - if (ServerUser != ServerInstance->FakeClient) + if (!IsRoot()) delete ServerUser; - 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()); } diff --git a/src/modules/m_spanningtree/treeserver.h b/src/modules/m_spanningtree/treeserver.h index 60b6d1def..ab47012b0 100644 --- a/src/modules/m_spanningtree/treeserver.h +++ b/src/modules/m_spanningtree/treeserver.h @@ -19,8 +19,7 @@ */ -#ifndef M_SPANNINGTREE_TREESERVER_H -#define M_SPANNINGTREE_TREESERVER_H +#pragma once #include "treesocket.h" @@ -38,74 +37,60 @@ * 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 */ + 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 + /** This method is used to add this TreeServer to the + * hash maps. It is only called by the constructors. */ - void SetID(const std::string &id); + void AddHashEntry(); 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); + TreeServer(const std::string& Name, const std::string& Desc, const std::string& id, TreeServer* Above, TreeSocket* Sock, bool Hide); int QuitUsers(const std::string &reason); - /** This method is used to add the structure to the - * hash_map for linear searches. It is only called - * by the constructors. - */ - void AddHashEntry(); - - /** 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 DelHashEntry(); - /** Get route. * The 'route' is defined as the locally- * connected server which can be used to reach this server. */ 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); } /** Get server version string */ @@ -143,22 +128,6 @@ class TreeServer : public classbase */ 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. */ @@ -173,13 +142,9 @@ class TreeServer : public classbase */ void SetVersion(const std::string &Version); - /** Return number of child servers + /** Return all child servers */ - unsigned int ChildCount(); - - /** Return a child server indexed 0..n - */ - TreeServer* GetChild(unsigned int n); + const ChildServers& GetChildren() const { return Children; } /** Add a child server */ @@ -193,7 +158,7 @@ class TreeServer : public classbase * 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(); + void Tidy(); /** Get server ID */ @@ -205,10 +170,24 @@ class TreeServer : public classbase /** Recursive call for child servers */ void FinishBurstInternal(); + /** (Re)check the uline state of this server + */ + void CheckULine(); + CullResult cull(); + /** Destructor + * Removes the reference to this object from the + * hash maps. */ ~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); + } +}; diff --git a/src/modules/m_spanningtree/treesocket.h b/src/modules/m_spanningtree/treesocket.h index d8445572b..4f72ed006 100644 --- a/src/modules/m_spanningtree/treesocket.h +++ b/src/modules/m_spanningtree/treesocket.h @@ -20,12 +20,9 @@ */ -#ifndef M_SPANNINGTREE_TREESOCKET_H -#define M_SPANNINGTREE_TREESOCKET_H +#pragma once -#include "socket.h" #include "inspircd.h" -#include "xline.h" #include "utils.h" @@ -92,7 +89,8 @@ struct CapabData */ 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) */ @@ -106,21 +104,31 @@ class TreeSocket : public BufferedSocket */ 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); + 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 */ @@ -166,11 +174,11 @@ class TreeSocket : public BufferedSocket * 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. @@ -180,13 +188,8 @@ class TreeSocket : public BufferedSocket /** 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 @@ -197,9 +200,6 @@ class TreeSocket : public BufferedSocket */ 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); @@ -220,9 +220,6 @@ class TreeSocket : public BufferedSocket */ 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. @@ -232,11 +229,8 @@ class TreeSocket : public BufferedSocket /** 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 @@ -252,57 +246,11 @@ class TreeSocket : public BufferedSocket /** 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); @@ -323,15 +271,16 @@ class TreeSocket : public BufferedSocket /** 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 */ bool Introduced(); -}; - -#endif + /** Fixes messages coming from old servers so the new command handlers understand them + */ + bool PreProcessOldProtocolMessage(User*& who, std::string& cmd, std::vector<std::string>& params); +}; diff --git a/src/modules/m_spanningtree/treesocket1.cpp b/src/modules/m_spanningtree/treesocket1.cpp index c9729cc0f..9da06e829 100644 --- a/src/modules/m_spanningtree/treesocket1.cpp +++ b/src/modules/m_spanningtree/treesocket1.cpp @@ -21,46 +21,30 @@ #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), ConnectionFailureShown(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); @@ -70,21 +54,16 @@ TreeSocket::TreeSocket(SpanningTreeUtilities* Util, Link* link, Autoconnect* mya * 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) + , ConnectionFailureShown(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); @@ -114,8 +93,7 @@ CullResult TreeSocket::cull() TreeSocket::~TreeSocket() { - if (capab) - delete capab; + delete capab; } /** When an outbound connection finishes connecting, we receive @@ -128,6 +106,17 @@ void TreeSocket::OnConnected() { 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); @@ -157,16 +146,15 @@ void TreeSocket::SendError(const std::string &errormessage) */ void TreeSocket::SquitServer(std::string &from, TreeServer* Current, int& num_lost_servers, int& num_lost_users) { - std::string servername = Current->GetName(); - ServerInstance->Logs->Log("m_spanningtree",DEBUG,"SquitServer for %s from %s", - servername.c_str(), from.c_str()); + ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "SquitServer for %s from %s", Current->GetName().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++) + 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; this->SquitServer(from,recursive_server, num_lost_servers, num_lost_users); } /* Now we've whacked the kids, whack self */ @@ -182,22 +170,21 @@ void TreeSocket::Squit(TreeServer* Current, const std::string &reason) { bool LocalSquit = false; - if ((Current) && (Current != Utils->TreeRoot)) + if (!Current->IsRoot()) { DelServerEvent(Utils->Creator, Current->GetName()); - 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()); - } - - if (Current->GetParent() == Utils->TreeRoot) + if (Current->IsLocal()) { ServerInstance->SNO->WriteGlobalSno('l', "Server \002"+Current->GetName()+"\002 split: "+reason); LocalSquit = true; + if (Current->GetSocket()->Introduced()) + { + CmdBuilder params("SQUIT"); + params.push_back(Current->GetID()); + params.push_last(reason); + params.Broadcast(); + } } else { @@ -206,7 +193,12 @@ void TreeSocket::Squit(TreeServer* Current, const std::string &reason) int num_lost_servers = 0; int num_lost_users = 0; std::string from = Current->GetParent()->GetName()+" "+Current->GetName(); + + ModuleSpanningTree* st = Utils->Creator; + st->SplitInProgress = true; SquitServer(from, Current, num_lost_servers, num_lost_users); + st->SplitInProgress = false; + 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(); @@ -220,8 +212,26 @@ void TreeSocket::Squit(TreeServer* Current, const std::string &reason) Close(); } } - else - ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"Squit from unknown server"); +} + +CmdResult CommandSQuit::HandleServer(TreeServer* server, std::vector<std::string>& params) +{ + TreeServer* quitting = Utils->FindServer(params[0]); + if (!quitting) + { + ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Squit from unknown server"); + return CMD_FAILURE; + } + + TreeSocket* sock = server->GetSocket(); + sock->Squit(quitting, params[1]); + + // XXX: Return CMD_FAILURE when servers SQUIT themselves (i.e. :00S SQUIT 00S :Shutting down) + // to avoid RouteCommand() being called. RouteCommand() requires a valid command source but we + // do not have one because the server user is deleted when its TreeServer is destructed. + // We generate a SQUIT in TreeSocket::Squit(), with our sid as the source and send it to the + // remaining servers. + return ((quitting == server) ? CMD_FAILURE : CMD_SUCCESS); } /** This function is called when we receive data from a remote diff --git a/src/modules/m_spanningtree/treesocket2.cpp b/src/modules/m_spanningtree/treesocket2.cpp index 5007fe921..4639a237e 100644 --- a/src/modules/m_spanningtree/treesocket2.cpp +++ b/src/modules/m_spanningtree/treesocket2.cpp @@ -23,16 +23,13 @@ #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) @@ -47,7 +44,7 @@ void TreeSocket::Split(const std::string& line, std::string& prefix, std::string if (!tokens.GetToken(prefix)) return; - + if (prefix[0] == ':') { prefix = prefix.substr(1); @@ -84,7 +81,7 @@ void TreeSocket::ProcessLine(std::string &line) 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); @@ -151,7 +148,7 @@ void TreeSocket::ProcessLine(std::string &line) { 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)) { @@ -176,20 +173,14 @@ void TreeSocket::ProcessLine(std::string &line) linkID = capab->name; - MyRoot = new TreeServer(Utils, capab->name, capab->description, capab->sid, Utils->TreeRoot, this, capab->hidden); + MyRoot = new TreeServer(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()); + CommandServer::Builder(MyRoot).Forward(MyRoot); + CmdBuilder(MyRoot->GetID(), "BURST").insert(params).Forward(MyRoot); } else if (command == "ERROR") { @@ -238,7 +229,6 @@ void TreeSocket::ProcessLine(std::string &line) void TreeSocket::ProcessConnectedLine(std::string& prefix, std::string& command, parameterlist& params) { User* who = ServerInstance->FindUUID(prefix); - std::string direction; if (!who) { @@ -257,7 +247,7 @@ void TreeSocket::ProcessConnectedLine(std::string& prefix, std::string& command, * crossing the users QUIT further upstream from the server. Thanks jilles! */ - if ((prefix.length() == UUID_LENGTH-1) && (isdigit(prefix[0])) && + 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 @@ -271,17 +261,13 @@ void TreeSocket::ProcessConnectedLine(std::string& prefix, std::string& command, } else { - ServerInstance->Logs->Log("m_spanningtree", DEBUG, "Command '%s' from unknown prefix '%s'! Dropping entire command.", + ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Command '%s' from unknown prefix '%s'! Dropping entire command.", command.c_str(), prefix.c_str()); return; } } } - // Make sure prefix is still good - direction = who->server; - prefix = who->uuid; - /* * Check for fake direction here, and drop any instances that are found. * What is fake direction? Imagine the following server setup: @@ -298,214 +284,73 @@ void TreeSocket::ProcessConnectedLine(std::string& prefix, std::string& command, * 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* route_back_again = TreeServer::Get(who)->GetRoute(); + if (route_back_again->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") + // Translate commands coming from servers using an older protocol + if (proto_version < ProtocolVersion) { - 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") - { - // 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") - { - this->Encap(who, params); - } - else if (command == "NICK") + ServerCommand* scmd = Utils->Creator->CmdManager.GetHandler(command); + CommandBase* cmdbase = scmd; + Command* cmd = NULL; + if (!scmd) { - 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. - */ - User* x = ServerInstance->FindNickOnly(params[0]); - if ((x) && (x != who)) + // 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, parsing or passing on this - * nickchange would be pointless, as the incoming client's server will - * soon recieve SVSNICK to change its nick to its UID. :) -- w00t - */ + this->Error(params); return; } - } - 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()); + + ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "Unrecognised S2S command :%s %s %s", + who->uuid.c_str(), command.c_str(), irc::stringjoiner(params).c_str()); SendError("Unrecognised command '" + command + "' -- possibly loaded mismatched modules"); return; } + cmdbase = cmd; + } - 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; - } + if (params.size() < cmdbase->min_params) + { + ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "Insufficient parameters for S2S command :%s %s %s", + who->uuid.c_str(), command.c_str(), irc::stringjoiner(params).c_str()); + SendError("Insufficient parameters for command '" + command + "'"); + return; + } - 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.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 = cmd->Handle(params, who); + 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); + if (res == CMD_INVALID) + { + ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "Error handling S2S command :%s %s %s", + who->uuid.c_str(), command.c_str(), irc::stringjoiner(params).c_str()); + SendError("Error handling '" + command + "' -- possibly loaded mismatched modules"); } + else if (res == CMD_SUCCESS) + Utils->RouteCommand(route_back_again, cmdbase, params, who); } void TreeSocket::OnTimeout() @@ -534,7 +379,7 @@ void TreeSocket::Close() time_t server_uptime = ServerInstance->Time() - this->age; if (server_uptime) { - std::string timestr = Utils->Creator->TimeToStr(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()); } } diff --git a/src/modules/m_spanningtree/uid.cpp b/src/modules/m_spanningtree/uid.cpp index f33cb038d..79bd6936f 100644 --- a/src/modules/m_spanningtree/uid.cpp +++ b/src/modules/m_spanningtree/uid.cpp @@ -23,14 +23,10 @@ #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 @@ -38,14 +34,10 @@ CmdResult CommandUID::Handle(const parameterlist ¶ms, User* serversrc) time_t age_t = ConvToInt(params[1]); time_t signon = ConvToInt(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) + if (params[0].length() != UIDGenerator::UUID_LENGTH || params[0].substr(0, 3) != remoteserver->GetID()) return CMD_INVALID; /* Check parameters for validity before introducing the client, discovered by dmb */ if (!age_t) @@ -54,27 +46,21 @@ CmdResult CommandUID::Handle(const parameterlist ¶ms, User* serversrc) return CMD_INVALID; if (modestr[0] != '+') return CMD_INVALID; - TreeSocket* sock = remoteserver->GetRoute()->GetSocket(); /* check for collision */ - user_hash::iterator iter = ServerInstance->Users->clientlist->find(params[2]); - - if (iter != ServerInstance->Users->clientlist->end()) + User* collideswith = ServerInstance->FindNickOnly(params[2]); + if (collideswith) { /* * Nick collision. */ - int collide = sock->DoCollision(iter->second, age_t, params[5], params[6], params[0]); - ServerInstance->Logs->Log("m_spanningtree",DEBUG,"*** Collision on %s, collide=%d", params[2].c_str(), collide); + int collide = Utils->DoCollision(collideswith, remoteserver, age_t, params[5], params[6], params[0]); + ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "*** Collision on %s, collide=%d", params[2].c_str(), collide); if (collide != 1) { - /* 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]; + // Remote client lost, make sure we change their nick for the hash too + params[2] = params[0]; } } @@ -84,14 +70,14 @@ CmdResult CommandUID::Handle(const parameterlist ¶ms, User* serversrc) User* _new = NULL; try { - _new = new RemoteUser(params[0], remoteserver->GetName()); + _new = new RemoteUser(params[0], remoteserver); } catch (...) { - ServerInstance->Logs->Log("m_spanningtree", DEFAULT, "Duplicate UUID %s in client introduction", params[0].c_str()); + ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Duplicate UUID %s in client introduction", params[0].c_str()); return CMD_INVALID; } - (*(ServerInstance->Users->clientlist))[params[2]] = _new; + ServerInstance->Users->clientlist[params[2]] = _new; _new->nick = params[2]; _new->host = params[3]; _new->dhost = params[4]; @@ -101,84 +87,90 @@ CmdResult CommandUID::Handle(const parameterlist ¶ms, User* serversrc) _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) + { + ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Unrecognised mode '%c' for a user in UID, dropping link", *v); + return CMD_INVALID; + } - 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) + 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(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->bursting) || _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); +} diff --git a/src/modules/m_spanningtree/utils.cpp b/src/modules/m_spanningtree/utils.cpp index 3bd0aa2c7..ee6f3fe74 100644 --- a/src/modules/m_spanningtree/utils.cpp +++ b/src/modules/m_spanningtree/utils.cpp @@ -21,16 +21,15 @@ #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) @@ -45,7 +44,7 @@ ModResult ModuleSpanningTree::OnAcceptConnection(int newsock, ListenSocket* from 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; } } @@ -61,10 +60,10 @@ ModResult ModuleSpanningTree::OnAcceptConnection(int newsock, ListenSocket* from */ 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; @@ -83,8 +82,6 @@ TreeServer* SpanningTreeUtilities::FindServer(const std::string &ServerName) */ 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) { @@ -96,9 +93,7 @@ TreeServer* SpanningTreeUtilities::BestRouteTo(const std::string &ServerName) User *u = ServerInstance->FindNick(ServerName); if (u) { - Found = FindServer(u->server); - if (Found) - return Found->GetRoute(); + return TreeServer::Get(u)->GetRoute(); } return NULL; @@ -130,24 +125,19 @@ TreeServer* SpanningTreeUtilities::FindServerID(const std::string &id) 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) @@ -165,19 +155,13 @@ SpanningTreeUtilities::~SpanningTreeUtilities() 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(); } @@ -194,86 +178,43 @@ void SpanningTreeUtilities::GetListOfServersForChannel(Channel* c, TreeServerLis 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); + ts->GetSocket()->WriteLine(params); } void SpanningTreeUtilities::RefreshIPCache() @@ -284,28 +225,27 @@ 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; } } } @@ -339,14 +279,18 @@ void SpanningTreeUtilities::ReadConfiguration() 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"); @@ -357,8 +301,8 @@ void SpanningTreeUtilities::ReadConfiguration() 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"); @@ -375,11 +319,11 @@ void SpanningTreeUtilities::ReadConfiguration() 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); @@ -390,7 +334,7 @@ void SpanningTreeUtilities::ReadConfiguration() { 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")); @@ -400,11 +344,6 @@ void SpanningTreeUtilities::ReadConfiguration() 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!"); @@ -413,6 +352,9 @@ void SpanningTreeUtilities::ReadConfiguration() AutoconnectBlocks.push_back(A); } + for (server_hash::const_iterator i = serverlist.begin(); i != serverlist.end(); ++i) + i->second->CheckULine(); + RefreshIPCache(); } @@ -428,3 +370,21 @@ Link* SpanningTreeUtilities::FindLink(const std::string& name) } return NULL; } + +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) +{ + 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); + } +} diff --git a/src/modules/m_spanningtree/utils.h b/src/modules/m_spanningtree/utils.h index a0543b6bd..4005f0db9 100644 --- a/src/modules/m_spanningtree/utils.h +++ b/src/modules/m_spanningtree/utils.h @@ -20,10 +20,10 @@ */ -#ifndef M_SPANNINGTREE_UTILS_H -#define M_SPANNINGTREE_UTILS_H +#pragma once #include "inspircd.h" +#include "cachetimer.h" /* Foward declarations */ class TreeServer; @@ -32,24 +32,24 @@ class Link; 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 @@ -124,31 +124,32 @@ class SpanningTreeUtilities : public classbase */ ~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); + int 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 */ @@ -173,6 +174,13 @@ class SpanningTreeUtilities : public classbase /** Refresh the IP cache used for allowing inbound connections */ void RefreshIPCache(); + + /** Sends a PRIVMSG or a NOTICE to a channel obeying an exempt list and an optional prefix + */ + 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); +} diff --git a/src/modules/m_spanningtree/version.cpp b/src/modules/m_spanningtree/version.cpp index e08d13e6e..193b51083 100644 --- a/src/modules/m_spanningtree/version.cpp +++ b/src/modules/m_spanningtree/version.cpp @@ -18,30 +18,14 @@ #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::ServerVersion(const std::string &prefix, parameterlist ¶ms) +CmdResult CommandVersion::HandleServer(TreeServer* server, std::vector<std::string>& params) { - 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; + server->SetVersion(params[0]); + return CMD_SUCCESS; } - diff --git a/src/modules/m_sqlauth.cpp b/src/modules/m_sqlauth.cpp index df97145be..1ffb3305a 100644 --- a/src/modules/m_sqlauth.cpp +++ b/src/modules/m_sqlauth.cpp @@ -18,10 +18,9 @@ #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, @@ -39,8 +38,8 @@ class AuthQuery : public SQLQuery : 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) @@ -57,7 +56,7 @@ class AuthQuery : public SQLQuery } } - void OnError(SQLerror& error) + void OnError(SQLerror& error) CXX11_OVERRIDE { User* user = ServerInstance->FindNick(uid); if (!user) @@ -83,15 +82,7 @@ class ModuleSQLAuth : public Module { } - 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"); @@ -105,7 +96,7 @@ class ModuleSQLAuth : public Module 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; @@ -139,12 +130,15 @@ class ModuleSQLAuth : public Module if (sha256) userinfo["sha256pass"] = sha256->hexsum(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)) { @@ -159,7 +153,7 @@ class ModuleSQLAuth : public Module return MOD_RES_PASSTHRU; } - Version GetVersion() + Version GetVersion() CXX11_OVERRIDE { return Version("Allow/Deny connections based upon an arbitrary SQL table", VF_VENDOR); } diff --git a/src/modules/m_sqloper.cpp b/src/modules/m_sqloper.cpp index ae581cc4b..fb5b65e56 100644 --- a/src/modules/m_sqloper.cpp +++ b/src/modules/m_sqloper.cpp @@ -18,24 +18,8 @@ #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 { @@ -46,9 +30,9 @@ 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; @@ -57,30 +41,17 @@ class OpMeQuery : public SQLQuery 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(); } @@ -101,16 +72,16 @@ class OpMeQuery : public SQLQuery } 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()) + OperIndex::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; @@ -119,7 +90,7 @@ class OpMeQuery : public SQLQuery 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. */ @@ -140,15 +111,7 @@ class ModuleSQLOper : public Module 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"); @@ -162,7 +125,7 @@ public: 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) { @@ -172,7 +135,7 @@ public: /* 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; } @@ -189,11 +152,10 @@ public: 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) diff --git a/src/modules/m_sslinfo.cpp b/src/modules/m_sslinfo.cpp index 2bfe0e1c4..656a9a432 100644 --- a/src/modules/m_sslinfo.cpp +++ b/src/modules/m_sslinfo.cpp @@ -18,9 +18,7 @@ #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: @@ -93,86 +91,80 @@ class CommandSSLInfo : public Command 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)) { @@ -184,7 +176,7 @@ class ModuleSSLInfo : public Module 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; } @@ -192,7 +184,7 @@ class ModuleSSLInfo : public Module 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 fingerprint."); user->CommandFloodPenalty += 10000; return MOD_RES_DENY; } @@ -203,15 +195,14 @@ class ModuleSSLInfo : public Module 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()) @@ -226,33 +217,23 @@ class ModuleSSLInfo : public Module } } - 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) - diff --git a/src/modules/m_sslmodes.cpp b/src/modules/m_sslmodes.cpp index c81c74207..6ac07434f 100644 --- a/src/modules/m_sslmodes.cpp +++ b/src/modules/m_sslmodes.cpp @@ -22,38 +22,44 @@ #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)) { + if (!API) + return MODEACTION_DENY; + const UserMembList* userlist = channel->GetUsers(); for(UserMembCIter 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 @@ -63,9 +69,9 @@ class SSLMode : public ModeHandler } else { - if (channel->IsModeSet('z')) + if (channel->IsModeSet(this)) { - channel->SetMode('z',false); + channel->SetMode(this, false); return MODEACTION_ALLOW; } @@ -85,20 +91,15 @@ class ModuleSSLModes : public Module { } - 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; @@ -106,7 +107,7 @@ class ModuleSSLModes : public Module 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; } } @@ -114,33 +115,29 @@ class ModuleSSLModes : public Module 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) - diff --git a/src/modules/m_starttls.cpp b/src/modules/m_starttls.cpp new file mode 100644 index 000000000..d591eed55 --- /dev/null +++ b/src/modules/m_starttls.cpp @@ -0,0 +1,121 @@ +/* + * 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) diff --git a/src/modules/m_stripcolor.cpp b/src/modules/m_stripcolor.cpp index f1504edaf..0d4bdb877 100644 --- a/src/modules/m_stripcolor.cpp +++ b/src/modules/m_stripcolor.cpp @@ -21,8 +21,6 @@ #include "inspircd.h" -/* $ModDesc: Provides channel +S mode (strip ansi color) */ - /** Handles channel mode +S */ class ChannelStripColor : public SimpleChannelModeHandler @@ -50,24 +48,12 @@ class ModuleStripColor : public Module { } - 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; @@ -76,7 +62,7 @@ class ModuleStripColor : public Module if (target_type == TYPE_USER) { User* t = (User*)dest; - active = t->IsModeSet('S'); + active = t->IsModeSet(usc); } else if (target_type == TYPE_CHANNEL) { @@ -86,7 +72,7 @@ class ModuleStripColor : public Module 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) @@ -97,12 +83,7 @@ class ModuleStripColor : public Module 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); } diff --git a/src/modules/m_svshold.cpp b/src/modules/m_svshold.cpp index e666b0fe2..af306ce62 100644 --- a/src/modules/m_svshold.cpp +++ b/src/modules/m_svshold.cpp @@ -23,8 +23,6 @@ #include "inspircd.h" #include "xline.h" -/* $ModDesc: Implements SVSHOLD. Like Q:Lines, but can only be added/removed by Services. */ - namespace { bool silent; @@ -35,16 +33,12 @@ namespace 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) @@ -56,23 +50,21 @@ public: 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; } }; @@ -104,7 +96,6 @@ class CommandSvshold : public Command 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) @@ -112,7 +103,7 @@ class CommandSvshold : public Command /* 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; @@ -127,7 +118,7 @@ class CommandSvshold : public Command } 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 @@ -135,8 +126,7 @@ class CommandSvshold : public Command 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)) @@ -151,7 +141,7 @@ class CommandSvshold : public Command 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()); } } @@ -182,22 +172,18 @@ class ModuleSVSHold : public Module { } - 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; @@ -206,26 +192,26 @@ class ModuleSVSHold : public Module return MOD_RES_DENY; } - virtual ModResult OnUserPreNick(User *user, const std::string &newnick) + ModResult OnUserPreNick(User *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); } diff --git a/src/modules/m_swhois.cpp b/src/modules/m_swhois.cpp index 742781747..4eb2a9cda 100644 --- a/src/modules/m_swhois.cpp +++ b/src/modules/m_swhois.cpp @@ -25,8 +25,6 @@ #include "inspircd.h" -/* $ModDesc: Provides the SWHOIS command which allows setting of arbitrary WHOIS lines */ - /** Handle /SWHOIS */ class CommandSwhois : public Command @@ -36,7 +34,7 @@ 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) @@ -45,7 +43,7 @@ class CommandSwhois : public Command 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; } @@ -53,11 +51,11 @@ class CommandSwhois : public Command 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()); @@ -90,16 +88,8 @@ class ModuleSWhois : public Module { } - 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) @@ -108,7 +98,7 @@ class ModuleSWhois : public Module 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()); } } @@ -116,7 +106,7 @@ class ModuleSWhois : public Module 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; @@ -130,11 +120,7 @@ class ModuleSWhois : public Module 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); } diff --git a/src/modules/m_testnet.cpp b/src/modules/m_testnet.cpp index 401766d8a..6e05ed681 100644 --- a/src/modules/m_testnet.cpp +++ b/src/modules/m_testnet.cpp @@ -17,167 +17,8 @@ */ -/* $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: @@ -199,11 +40,6 @@ class CommandTest : public Command { 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; } }; @@ -216,18 +52,16 @@ class ModuleTest : public Module { } - 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) - diff --git a/src/modules/m_timedbans.cpp b/src/modules/m_timedbans.cpp index 497ac2569..e3a938336 100644 --- a/src/modules/m_timedbans.cpp +++ b/src/modules/m_timedbans.cpp @@ -20,8 +20,6 @@ */ -/* $ModDesc: Adds timed bans */ - #include "inspircd.h" /** Holds a timed ban @@ -45,7 +43,6 @@ class CommandTban : public Command 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) @@ -53,24 +50,24 @@ class CommandTban : public Command 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]; @@ -78,22 +75,19 @@ class CommandTban : public Command 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)) + + setban.push_back(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(setban, user); + 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; @@ -114,27 +108,22 @@ found: } }; -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(); @@ -144,10 +133,22 @@ class ModuleTimedBans : public Module break; } } - return MOD_RES_PASSTHRU; + } +}; + +class ModuleTimedBans : public Module +{ + CommandTban cmd; + BanWatcher banwatcher; + + public: + ModuleTimedBans() + : cmd(this) + , banwatcher(this) + { } - virtual void OnBackgroundTimer(time_t curtime) + void OnBackgroundTimer(time_t curtime) CXX11_OVERRIDE { timedbans expired; for (timedbans::iterator i = TimedBanList.begin(); i != TimedBanList.end();) @@ -178,16 +179,15 @@ class ModuleTimedBans : public Module 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); + ServerInstance->Modes->Process(setban, ServerInstance->FakeClient); } } } - virtual Version GetVersion() + Version GetVersion() CXX11_OVERRIDE { return Version("Adds timed bans", VF_COMMON | VF_VENDOR); } }; MODULE_INIT(ModuleTimedBans) - diff --git a/src/modules/m_tline.cpp b/src/modules/m_tline.cpp index b4e7e5a99..77ec0e26c 100644 --- a/src/modules/m_tline.cpp +++ b/src/modules/m_tline.cpp @@ -20,8 +20,6 @@ #include "inspircd.h" -/* $ModDesc: Provides /tline command used to test who a mask matches */ - /** Handle /TLINE */ class CommandTline : public Command @@ -34,14 +32,13 @@ 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++; @@ -57,10 +54,15 @@ class CommandTline : public Command } } } + + 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; } @@ -75,20 +77,10 @@ class ModuleTLine : public Module { } - 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) - diff --git a/src/modules/m_topiclock.cpp b/src/modules/m_topiclock.cpp index 3e8a846e7..42ed6e4c1 100644 --- a/src/modules/m_topiclock.cpp +++ b/src/modules/m_topiclock.cpp @@ -16,8 +16,6 @@ * 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 @@ -31,7 +29,7 @@ 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; @@ -47,7 +45,7 @@ class CommandSVSTOPIC : public Command 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; } @@ -96,10 +94,6 @@ class FlagExtItem : public ExtensionItem { } - virtual ~FlagExtItem() - { - } - bool get(const Extensible* container) const { return (get_raw(container) != NULL); @@ -151,14 +145,7 @@ class ModuleTopicLock : public Module { } - 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))) @@ -170,7 +157,7 @@ class ModuleTopicLock : public Module 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); } diff --git a/src/modules/m_uhnames.cpp b/src/modules/m_uhnames.cpp index 2cd090f97..2534afb97 100644 --- a/src/modules/m_uhnames.cpp +++ b/src/modules/m_uhnames.cpp @@ -20,9 +20,7 @@ #include "inspircd.h" -#include "m_cap.h" - -/* $ModDesc: Provides the UHNAMES facility. */ +#include "modules/cap.h" class ModuleUHNames : public Module { @@ -33,27 +31,17 @@ 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. @@ -71,7 +59,7 @@ class ModuleUHNames : public Module return MOD_RES_PASSTHRU; } - void OnNamesListItem(User* issuer, Membership* memb, std::string &prefixes, std::string &nick) + void OnNamesListItem(User* issuer, Membership* memb, std::string &prefixes, std::string &nick) CXX11_OVERRIDE { if (!cap.ext.get(issuer)) return; @@ -82,7 +70,7 @@ class ModuleUHNames : public Module nick = memb->user->GetFullHost(); } - void OnEvent(Event& ev) + void OnEvent(Event& ev) CXX11_OVERRIDE { cap.HandleEvent(ev); } diff --git a/src/modules/m_uninvite.cpp b/src/modules/m_uninvite.cpp index ff392edc3..97ad841f1 100644 --- a/src/modules/m_uninvite.cpp +++ b/src/modules/m_uninvite.cpp @@ -20,8 +20,6 @@ */ -/* $ModDesc: Provides the UNINVITE command which lets users un-invite other users from channels (!) */ - #include "inspircd.h" /** Handle /UNINVITE @@ -32,7 +30,7 @@ class CommandUninvite : public Command 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) @@ -49,11 +47,11 @@ class CommandUninvite : public Command { 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; @@ -63,7 +61,7 @@ class CommandUninvite : public Command { 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; } } @@ -75,16 +73,14 @@ class CommandUninvite : public Command 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); @@ -111,20 +107,10 @@ class ModuleUninvite : public Module { } - 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) - diff --git a/src/modules/m_userip.cpp b/src/modules/m_userip.cpp index 33b261ca1..96505a047 100644 --- a/src/modules/m_userip.cpp +++ b/src/modules/m_userip.cpp @@ -21,8 +21,6 @@ #include "inspircd.h" -/* $ModDesc: Provides support for USERIP command */ - /** Handle /USERIP */ class CommandUserip : public Command @@ -54,15 +52,15 @@ 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 += "+"; @@ -87,28 +85,15 @@ class ModuleUserIP : public Module { } - 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) - diff --git a/src/modules/m_vhost.cpp b/src/modules/m_vhost.cpp index 31c504af8..53910fdbe 100644 --- a/src/modules/m_vhost.cpp +++ b/src/modules/m_vhost.cpp @@ -22,8 +22,6 @@ #include "inspircd.h" -/* $ModDesc: Provides masking of user hostnames via traditional /VHOST command */ - /** Handle /VHOST */ class CommandVhost : public Command @@ -45,25 +43,24 @@ 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: @@ -71,22 +68,10 @@ class ModuleVHost : public Module { } - 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) - diff --git a/src/modules/m_watch.cpp b/src/modules/m_watch.cpp index 0e532d65b..57ca18a8f 100644 --- a/src/modules/m_watch.cpp +++ b/src/modules/m_watch.cpp @@ -22,8 +22,6 @@ #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 @@ -92,12 +90,7 @@ * 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. @@ -112,12 +105,12 @@ class CommandSVSWatch : public Command 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]); @@ -151,9 +144,9 @@ class CommandWatch : public Command 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; } @@ -169,9 +162,9 @@ class CommandWatch : public Command 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); } @@ -201,9 +194,9 @@ class CommandWatch : public Command 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; } @@ -216,7 +209,7 @@ class CommandWatch : public Command 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; } @@ -241,16 +234,16 @@ class CommandWatch : public Command 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); } } @@ -260,7 +253,6 @@ class CommandWatch : public Command 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) @@ -273,10 +265,10 @@ class CommandWatch : public Command 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) { @@ -319,17 +311,17 @@ class CommandWatch : public Command 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")) { @@ -349,9 +341,9 @@ class CommandWatch : public Command 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] == '-') { @@ -382,24 +374,14 @@ class Modulewatch : public Module 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; @@ -427,14 +409,14 @@ class Modulewatch : public Module 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) @@ -467,7 +449,7 @@ class Modulewatch : public Module } } - virtual void OnGarbageCollect() + void OnGarbageCollect() { watchentries* old_watch = whos_watching_me; whos_watching_me = new watchentries(); @@ -478,14 +460,14 @@ class Modulewatch : public Module 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) @@ -495,7 +477,7 @@ class Modulewatch : public Module } } - 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()); @@ -507,7 +489,7 @@ class Modulewatch : public Module 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(); } } @@ -521,28 +503,26 @@ class Modulewatch : public Module 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) - diff --git a/src/modules/m_xline_db.cpp b/src/modules/m_xline_db.cpp index 5e83478c3..1a7fd8cc5 100644 --- a/src/modules/m_xline_db.cpp +++ b/src/modules/m_xline_db.cpp @@ -20,45 +20,36 @@ #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; } @@ -68,17 +59,17 @@ class ModuleXLineDB : public Module * @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) { @@ -89,25 +80,23 @@ class ModuleXLineDB : public Module 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->Logs->Log(MODNAME, LOG_DEBUG, "Cannot create database! %s (%d)", strerror(errno), errno); ServerInstance->SNO->WriteToSnoMask('a', "database: cannot create new db: %s (%d)", 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 @@ -116,7 +105,7 @@ class ModuleXLineDB : public Module * 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(); @@ -129,27 +118,26 @@ class ModuleXLineDB : public Module 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->Logs->Log(MODNAME, LOG_DEBUG, "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); return false; } + stream.close(); #ifdef _WIN32 if (remove(xlinedbpath.c_str())) { - ServerInstance->Logs->Log("m_xline_db",DEBUG, "xlinedb: Cannot remove old database! %s (%d)", strerror(errno), errno); + ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Cannot remove old database! %s (%d)", strerror(errno), errno); ServerInstance->SNO->WriteToSnoMask('a', "database: cannot remove old database: %s (%d)", strerror(errno), errno); return false; } @@ -157,7 +145,7 @@ class ModuleXLineDB : public Module // 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->Logs->Log(MODNAME, LOG_DEBUG, "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); return false; } @@ -167,42 +155,23 @@ class ModuleXLineDB : public Module 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 (%d)", strerror(errno), errno); + ServerInstance->SNO->WriteToSnoMask('a', "database: cannot read db: %s (%d)", 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; @@ -213,18 +182,14 @@ class ModuleXLineDB : public Module 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; } @@ -251,18 +216,14 @@ class ModuleXLineDB : public Module 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) - diff --git a/src/modules/u_listmode.h b/src/modules/u_listmode.h deleted file mode 100644 index 0f5903e53..000000000 --- a/src/modules/u_listmode.h +++ /dev/null @@ -1,423 +0,0 @@ -/* - * 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); - } - if (chanlimits.empty()) - { - 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 diff --git a/src/server.cpp b/src/server.cpp index 4741f942d..9e58c9cff 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -23,7 +23,6 @@ #include <signal.h> #include "exitcodes.h" #include "inspircd.h" -#include "inspircd_version.h" void InspIRCd::SignalHandler(int signal) { @@ -32,12 +31,13 @@ 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); } } @@ -56,48 +56,37 @@ void InspIRCd::Exit(int status) 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->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 VERSION " " + Config->ServerName + " :" SYSTEM " [" REVISION "," INSPIRCD_SOCKETENGINE_NAME "," + Config->sid + "]"; + return 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... @@ -106,85 +95,140 @@ void InspIRCd::IncrementUID(int pos) * 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') + { + current_uid[pos] = '0'; + } + else if (current_uid[pos] == '9') { - // 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++) + /* + * 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"] = + tokens["WALLCHOPS"] = tokens["WALLVOICES"]; + + // 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()) + { + 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::vector<std::string>& lines = this->Lines; + std::string line; + unsigned int token_count = 0; + lines.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"); + lines.push_back(line); + line.clear(); + } + } +} + +void ISupportManager::SendTo(LocalUser* user) +{ + for (std::vector<std::string>::const_iterator i = this->Lines.begin(); i != this->Lines.end(); ++i) + user->WriteNumeric(RPL_ISUPPORT, *i); +} diff --git a/src/snomasks.cpp b/src/snomasks.cpp index 4b9c9d86b..738d0970d 100644 --- a/src/snomasks.cpp +++ b/src/snomasks.cpp @@ -47,31 +47,21 @@ void SnomaskManager::WriteGlobalSno(char letter, const std::string& text) { 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() @@ -79,54 +69,42 @@ 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++; } @@ -134,36 +112,44 @@ void Snomask::Flush() { 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; +} diff --git a/src/socket.cpp b/src/socket.cpp index e73d01af9..4ff43cde7 100644 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -46,7 +46,7 @@ bool InspIRCd::BindSocket(int sockfd, int port, const char* addr, bool dolisten) else if (!irc::sockets::aptosa(addr, port, servaddr)) return false; - ret = SE->Bind(sockfd, servaddr); + ret = SocketEngine::Bind(sockfd, servaddr); if (ret < 0) { @@ -56,21 +56,21 @@ bool InspIRCd::BindSocket(int sockfd, int port, const char* addr, bool dolisten) { if (dolisten) { - if (SE->Listen(sockfd, Config->MaxConn) == -1) + if (SocketEngine::Listen(sockfd, Config->MaxConn) == -1) { - this->Logs->Log("SOCKET",DEFAULT,"ERROR in listen(): %s",strerror(errno)); + this->Logs->Log("SOCKET", LOG_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); + this->Logs->Log("SOCKET", LOG_DEBUG, "New socket binding for %d with listen: %s:%d", sockfd, addr, port); + SocketEngine::NonBlocking(sockfd); return true; } } else { - this->Logs->Log("SOCKET",DEBUG,"New socket binding for %d without listen: %s:%d", sockfd, addr, port); + this->Logs->Log("SOCKET", LOG_DEBUG, "New socket binding for %d without listen: %s:%d", sockfd, addr, port); return true; } } @@ -89,7 +89,7 @@ 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; @@ -106,6 +106,8 @@ int InspIRCd::BindPorts(FailedPortList &failed_ports) 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; @@ -136,11 +138,11 @@ int InspIRCd::BindPorts(FailedPortList &failed_ports) 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; @@ -219,24 +221,22 @@ bool irc::sockets::satoap(const irc::sockets::sockaddrs& sa, std::string& addr, 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 @@ -367,4 +367,3 @@ bool irc::sockets::cidr_mask::match(const irc::sockets::sockaddrs& addr) const irc::sockets::cidr_mask tmp(addr, length); return tmp == *this; } - diff --git a/src/socketengine.cpp b/src/socketengine.cpp index 8af598b06..86608f0a3 100644 --- a/src/socketengine.cpp +++ b/src/socketengine.cpp @@ -23,6 +23,25 @@ #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; @@ -34,22 +53,6 @@ void EventHandler::SetFd(int FD) 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; @@ -60,7 +63,7 @@ void SocketEngine::ChangeEventMask(EventHandler* eh, int change) 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()); @@ -94,17 +97,38 @@ void SocketEngine::DispatchTrialWrites() } } -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]; } @@ -123,13 +147,12 @@ int SocketEngine::Accept(EventHandler* fd, sockaddr *addr, socklen_t *addrlen) 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) @@ -173,7 +196,7 @@ int SocketEngine::RecvFrom(EventHandler* fd, void *buf, size_t len, int flags, s { int nbRecvd = recvfrom(fd->GetFd(), (char*)buf, len, flags, from, fromlen); if (nbRecvd > 0) - this->UpdateStats(nbRecvd, 0); + stats.Update(nbRecvd, 0); return nbRecvd; } @@ -181,7 +204,7 @@ int SocketEngine::Send(EventHandler* fd, const void *buf, size_t len, int flags) { int nbSent = send(fd->GetFd(), (const char*)buf, len, flags); if (nbSent > 0) - this->UpdateStats(0, nbSent); + stats.Update(0, nbSent); return nbSent; } @@ -189,7 +212,7 @@ int SocketEngine::Recv(EventHandler* fd, void *buf, size_t len, int flags) { int nbRecvd = recv(fd->GetFd(), (char*)buf, len, flags); if (nbRecvd > 0) - this->UpdateStats(nbRecvd, 0); + stats.Update(nbRecvd, 0); return nbRecvd; } @@ -197,7 +220,7 @@ int SocketEngine::SendTo(EventHandler* fd, const void *buf, size_t len, int flag { 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; } @@ -231,24 +254,27 @@ int SocketEngine::Shutdown(int fd, int how) 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); diff --git a/src/socketengines/socketengine_epoll.cpp b/src/socketengines/socketengine_epoll.cpp index f2837777a..26dfc8f2f 100644 --- a/src/socketengines/socketengine_epoll.cpp +++ b/src/socketengines/socketengine_epoll.cpp @@ -31,30 +31,17 @@ /** 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) { @@ -62,7 +49,7 @@ EPollEngine::EPollEngine() } else { - ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets!"); + ServerInstance->Logs->Log("SOCKET", LOG_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); } @@ -72,24 +59,21 @@ EPollEngine::EPollEngine() 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) @@ -115,41 +99,41 @@ 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)) { - 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, max: %d)", fd, GetMaxFds()); 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); @@ -157,75 +141,78 @@ void EPollEngine::OnSetEvent(EventHandler* eh, int old_mask, int 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)) { - 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, max: %d)", fd, GetMaxFds()); 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) @@ -235,31 +222,21 @@ int EPollEngine::DispatchEvents() 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; -} diff --git a/src/socketengines/socketengine_kqueue.cpp b/src/socketengines/socketengine_kqueue.cpp index 8694a0bdd..b7797a82f 100644 --- a/src/socketengines/socketengine_kqueue.cpp +++ b/src/socketengines/socketengine_kqueue.cpp @@ -26,37 +26,26 @@ #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]; @@ -72,18 +61,15 @@ KQueueEngine::KQueueEngine() 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!"); + ServerInstance->Logs->Log("SOCKET", LOG_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. @@ -93,176 +79,148 @@ void KQueueEngine::RecoverFromFork() 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)) 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)) { - 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)); - } - - 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); } -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; -} diff --git a/src/socketengines/socketengine_poll.cpp b/src/socketengines/socketengine_poll.cpp index e38e0fac1..ac5e02cfd 100644 --- a/src/socketengines/socketengine_poll.cpp +++ b/src/socketengines/socketengine_poll.cpp @@ -1,6 +1,7 @@ /* * 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> @@ -20,17 +21,11 @@ */ -#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 "exitcodes.h" #include "inspircd.h" #include "socketengine.h" @@ -46,39 +41,20 @@ # define poll WSAPoll #endif -class InspIRCd; - /** 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::vector<struct pollfd> events(16); + /** This vector maps fds to an index in the events array. */ - std::map<int, unsigned int> fd_mappings; -public: - /** Create a new PollEngine - */ - 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 + std::vector<int> fd_mappings(16); +} -PollEngine::PollEngine() +void SocketEngine::Init() { - CurrentSetSize = 0; struct rlimit limits; if (!getrlimit(RLIMIT_NOFILE, &limits)) { @@ -86,23 +62,18 @@ PollEngine::PollEngine() } else { - ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets: %s", strerror(errno)); + ServerInstance->Logs->Log("SOCKET", LOG_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); } +} - 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) @@ -115,71 +86,70 @@ 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)) { - 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, max: %d)", fd, GetMaxFds()); 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) -{ - 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) +void SocketEngine::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)) { - 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, max: %d)", fd, GetMaxFds()); 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; @@ -193,89 +163,78 @@ void PollEngine::DelFd(EventHandler* eh) // 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; -} diff --git a/src/socketengines/socketengine_ports.cpp b/src/socketengines/socketengine_ports.cpp index f7c547d45..c30400107 100644 --- a/src/socketengines/socketengine_ports.cpp +++ b/src/socketengines/socketengine_ports.cpp @@ -20,10 +20,6 @@ #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. @@ -32,41 +28,25 @@ #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) @@ -75,7 +55,7 @@ PortsEngine::PortsEngine() } else { - ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets!"); + ServerInstance->Logs->Log("SOCKET", LOG_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); } @@ -83,24 +63,23 @@ PortsEngine::PortsEngine() 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) @@ -113,31 +92,31 @@ 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)) 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)) @@ -145,13 +124,12 @@ void PortsEngine::DelFd(EventHandler* eh) 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; @@ -159,62 +137,51 @@ int PortsEngine::DispatchEvents() 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) + { + stats.ReadEvents++; + eh->HandleEvent(EVENT_READ); + if (eh != GetRef(fd)) + continue; + } + if (portev_events & POLLWRNORM) { - 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.WriteEvents++; + eh->HandleEvent(EVENT_WRITE); } } return (int)i; } - -std::string PortsEngine::GetName() -{ - return "ports"; -} - -SocketEngine* CreateSocketEngine() -{ - return new PortsEngine; -} diff --git a/src/socketengines/socketengine_select.cpp b/src/socketengines/socketengine_select.cpp index 0b5abaf30..be4a7d186 100644 --- a/src/socketengines/socketengine_select.cpp +++ b/src/socketengines/socketengine_select.cpp @@ -1,6 +1,7 @@ /* * 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> * @@ -18,8 +19,6 @@ */ -#include "inspircd_config.h" - #include "inspircd.h" #include "socketengine.h" @@ -29,76 +28,56 @@ /** 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); @@ -106,10 +85,10 @@ void SelectEngine::DelFd(EventHandler* eh) 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; @@ -130,7 +109,7 @@ void SelectEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask) } } -int SelectEngine::DispatchEvents() +int SocketEngine::DispatchEvents() { timeval tval; tval.tv_sec = 1; @@ -141,63 +120,50 @@ int SelectEngine::DispatchEvents() 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; -} diff --git a/src/testsuite.cpp b/src/testsuite.cpp index 58b72ee3e..b57a21ab8 100644 --- a/src/testsuite.cpp +++ b/src/testsuite.cpp @@ -19,7 +19,7 @@ */ -/* $Core */ +#ifdef INSPIRCD_ENABLE_TESTSUITE #include "inspircd.h" #include "testsuite.h" @@ -76,8 +76,12 @@ TestSuite::TestSuite() 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; @@ -331,36 +335,25 @@ bool TestSuite::DoThreadTests() 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; @@ -368,16 +361,16 @@ bool TestSuite::RealGenerateUIDTests() } // 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) { @@ -386,11 +379,11 @@ bool TestSuite::RealGenerateUIDTests() } // 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) @@ -407,3 +400,4 @@ TestSuite::~TestSuite() std::cout << "\n\n*** END OF TEST SUITE ***\n"; } +#endif diff --git a/src/threadengine.cpp b/src/threadengine.cpp index 8f1895c0f..8e1bac470 100644 --- a/src/threadengine.cpp +++ b/src/threadengine.cpp @@ -17,12 +17,6 @@ */ -/* $Core */ - -/********* DEFAULTS **********/ -/* $ExtraSources: threadengines/threadengine_pthread.cpp */ -/* $ExtraObjects: threadengine_pthread.o */ - #include "inspircd.h" #include "threadengine.h" diff --git a/src/threadengines/threadengine_pthread.cpp b/src/threadengines/threadengine_pthread.cpp index e16e401f3..8527907c4 100644 --- a/src/threadengines/threadengine_pthread.cpp +++ b/src/threadengines/threadengine_pthread.cpp @@ -75,13 +75,12 @@ class ThreadSignalSocket : public EventHandler 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() @@ -123,15 +122,14 @@ class ThreadSignalSocket : public EventHandler 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() diff --git a/src/threadengines/threadengine_win32.cpp b/src/threadengines/threadengine_win32.cpp index ea37892f8..c3a844a74 100644 --- a/src/threadengines/threadengine_win32.cpp +++ b/src/threadengines/threadengine_win32.cpp @@ -94,20 +94,20 @@ SocketThread::SocketThread() if (!ServerInstance->BindSocket(listenFD, 0, "127.0.0.1", true)) 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; } diff --git a/src/timer.cpp b/src/timer.cpp index e04a186cf..8e11ee4a7 100644 --- a/src/timer.cpp +++ b/src/timer.cpp @@ -20,60 +20,58 @@ */ -/* $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() { - for(std::vector<Timer *>::iterator i = Timers.begin(); i != Timers.end(); i++) - delete *i; + 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)); } diff --git a/src/user_resolver.cpp b/src/user_resolver.cpp deleted file mode 100644 index f18fc9a03..000000000 --- a/src/user_resolver.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/* - * 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++; - } -} diff --git a/src/usermanager.cpp b/src/usermanager.cpp index 076277a33..5dc410fff 100644 --- a/src/usermanager.cpp +++ b/src/usermanager.cpp @@ -23,12 +23,21 @@ #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) { @@ -42,46 +51,33 @@ void UserManager::AddUser(int socket, ListenSocket* via, irc::sockets::sockaddrs } 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) || (this->local_users.size() >= (unsigned int)SocketEngine::GetMaxFds())) { ServerInstance->SNO->WriteToSnoMask('a', "Warning: softlimit value has been reached: %d clients", ServerInstance->Config->SoftLimit); this->QuitUser(New,"No more connections allowed"); @@ -98,7 +94,7 @@ void UserManager::AddUser(int socket, ListenSocket* via, irc::sockets::sockaddrs * 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; @@ -114,15 +110,15 @@ void UserManager::AddUser(int socket, ListenSocket* via, irc::sockets::sockaddrs 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 @@ -139,260 +135,208 @@ void UserManager::AddUser(int socket, ListenSocket* via, irc::sockets::sockaddrs } } - 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()); } - 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); - - snprintf(formatbuffer,MAXBUF,"NOTICE $%s :%s", ServerInstance->Config->ServerName.c_str(), textbuffer); + std::string message; + VAFORMAT(message, text, text); + message = "NOTICE $" + ServerInstance->Config->ServerName + " :" + message; for (LocalUserList::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 (LocalUserList::const_iterator i = this->local_users.begin(); i != this->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 (LocalUserList::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->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 (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; } diff --git a/src/userprocess.cpp b/src/userprocess.cpp deleted file mode 100644 index 9cc2e7df7..000000000 --- a/src/userprocess.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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; - } - } -} - diff --git a/src/users.cpp b/src/users.cpp index 573d4db90..a15c31727 100644 --- a/src/users.cpp +++ b/src/users.cpp @@ -28,108 +28,9 @@ #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)) @@ -137,55 +38,25 @@ bool User::IsNoticeMaskSet(unsigned char 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()) @@ -193,36 +64,32 @@ const char* User::FormatModes(bool showparameters) } } } - 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); @@ -233,8 +100,8 @@ LocalUser::LocalUser(int myfd, irc::sockets::sockaddrs* client, irc::sockets::so 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()); + if (ServerInstance->FindUUID(uuid)) + ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "User destructor for %s called without cull", uuid.c_str()); } const std::string& User::MakeHost() @@ -242,18 +109,8 @@ 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; } @@ -262,18 +119,8 @@ const std::string& User::MakeHostIP() 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; } @@ -282,91 +129,36 @@ const std::string& User::GetFullHost() 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() @@ -381,7 +173,7 @@ bool User::HasModePermission(unsigned char, ModeType) 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; @@ -404,7 +196,7 @@ bool User::HasPermission(const std::string&) bool LocalUser::HasPermission(const std::string &command) { // are they even an oper at all? - if (!IS_OPER(this)) + if (!this->IsOper()) { return false; } @@ -424,10 +216,10 @@ bool User::HasPrivPermission(const std::string &privstr, bool noisy) 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; } @@ -441,7 +233,8 @@ bool LocalUser::HasPrivPermission(const std::string &privstr, bool noisy) } 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; } @@ -467,7 +260,7 @@ void UserIOHandler::OnDataReady() 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()) { @@ -482,7 +275,7 @@ void UserIOHandler::OnDataReady() 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 @@ -531,7 +324,6 @@ CullResult User::cull() { if (!quitting) ServerInstance->Users->QuitUser(this, "Culled without QuitUser"); - PurgeEmptyChannels(); if (client_sa.sa.sa_family != AF_UNSPEC) ServerInstance->Users->RemoveCloneCounts(this); @@ -541,17 +333,7 @@ CullResult User::cull() 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); - + ServerInstance->Users->local_users.erase(this); ClearInvites(); eh.cull(); return User::cull(); @@ -561,20 +343,21 @@ CullResult FakeUser::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 + ServerInstance->Users->uuidlist.erase(uuid); 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) @@ -592,17 +375,17 @@ void User::Oper(OperInfo* info) } 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() @@ -660,7 +443,7 @@ void OperInfo::init() void User::UnOper() { - if (!IS_OPER(this)) + if (!this->IsOper()) return; /* @@ -686,30 +469,19 @@ void User::UnOper() parameters.push_back(this->nick); parameters.push_back(moderemove); - ServerInstance->Parser->CallHandler("MODE", parameters, this); + ServerInstance->Modes->Process(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; @@ -723,25 +495,29 @@ void LocalUser::CheckClass() 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 }; @@ -781,19 +557,14 @@ void LocalUser::FullConnect() 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(), 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(), 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) @@ -801,14 +572,14 @@ void LocalUser::FullConnect() /* 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); 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); @@ -820,15 +591,15 @@ void LocalUser::FullConnect() * 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()); + 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; @@ -843,31 +614,31 @@ void User::InvalidateCache() cached_fullrealhost.clear(); } -bool User::ChangeNick(const std::string& newnick, bool force) +bool User::ChangeNick(const std::string& newnick, bool force, time_t newts) { if (quitting) { - ServerInstance->Logs->Log("USERS", DEFAULT, "ERROR: Attempted to change nick of a quitting user: " + this->nick); + ServerInstance->Logs->Log("USERS", LOG_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) + if (!force) { - ServerInstance->stats->statsCollisions++; - return false; + ModResult MOD_RESULT; + FIRST_MOD_RESULT(OnUserPreNick, MOD_RESULT, (this, newnick)); + + if (MOD_RESULT == MOD_RES_DENY) + { + ServerInstance->stats->statsCollisions++; + return false; + } } if (assign(newnick) == assign(nick)) { // case change, don't need to check Q:lines and such // 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; } @@ -891,7 +662,7 @@ bool User::ChangeNick(const std::string& newnick, bool force) 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()); + this->WriteNumeric(ERR_ERRONEUSNICKNAME, "%s :Invalid nickname: %s", newnick.c_str(), mq->reason.c_str()); return false; } @@ -899,10 +670,10 @@ bool User::ChangeNick(const std::string& newnick, bool force) { for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++) { - Channel *chan = *i; + Channel* chan = (*i)->chan; 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()); + this->WriteNumeric(ERR_CANNOTSENDTOCHAN, "%s :Cannot send to channel (you're banned)", chan->name.c_str()); return false; } } @@ -924,11 +695,11 @@ bool User::ChangeNick(const std::string& newnick, bool force) 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()); + InUse->WriteFrom(InUse, "NICK %s", InUse->uuid.c_str()); + InUse->WriteNumeric(ERR_NICKNAMEINUSE, "%s :Nickname overruled.", InUse->nick.c_str()); - ServerInstance->Users->clientlist->erase(InUse->nick); - (*(ServerInstance->Users->clientlist))[InUse->uuid] = InUse; + ServerInstance->Users->clientlist.erase(InUse->nick); + ServerInstance->Users->clientlist[InUse->uuid] = InUse; InUse->nick = InUse->uuid; InUse->InvalidateCache(); @@ -937,10 +708,12 @@ bool User::ChangeNick(const std::string& newnick, bool force) 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) @@ -949,11 +722,11 @@ bool User::ChangeNick(const std::string& newnick, bool force) 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; } @@ -970,18 +743,18 @@ int LocalUser::GetServerPort() 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() @@ -1032,7 +805,7 @@ void LocalUser::SetClientIP(const irc::sockets::sockaddrs& sa, bool recheck_elin if (recheck_eline) this->exempt = (ServerInstance->XLines->MatchesLine("E", this) != NULL); - FOREACH_MOD(I_OnSetUserIP,OnSetUserIP(this)); + FOREACH_MOD(OnSetUserIP, (this)); } } @@ -1048,18 +821,18 @@ void User::Write(const char *text, ...) 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); @@ -1073,14 +846,9 @@ void LocalUser::Write(const std::string& text) */ 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) @@ -1093,32 +861,25 @@ 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)); @@ -1126,17 +887,15 @@ void User::WriteNumeric(unsigned int numeric, const std::string &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); } @@ -1144,68 +903,20 @@ void User::WriteFrom(User *user, const std::string &text) 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) @@ -1215,12 +926,12 @@ 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) { @@ -1232,14 +943,14 @@ void User::WriteCommonRaw(const std::string &line, bool include_self) 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; + Channel* c = (*v)->chan; const UserMembList* ulist = c->GetUsers(); for (UserMembList::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); @@ -1250,23 +961,18 @@ void User::WriteCommonRaw(const std::string &line, bool include_self) 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) { @@ -1275,19 +981,19 @@ void User::WriteCommonQuit(const std::string &normal_text, const std::string &op { 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(); + const UserMembList* ulist = (*v)->chan->GetUsers(); for (UserMembList::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); } } } @@ -1309,38 +1015,26 @@ void FakeUser::SendText(const std::string& line) 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) { - char line[MAXBUF]; - int start_pos = LinePrefix.length(); - int pos = start_pos; - memcpy(line, LinePrefix.data(), pos); - std::string Word; - while (TextStream >> Word) + std::string line; + std::string word; + while (textStream >> word) { - int len = Word.length(); - if (pos + len + 12 > MAXBUF) + size_t lineLength = linePrefix.length() + line.length() + word.length() + 3; // "\s\n\r" + if (lineLength > ServerInstance->Config->Limits.MaxLine) { - line[pos] = '\0'; - SendText(std::string(line)); - pos = start_pos; + SendText(linePrefix + line); + line.clear(); } - line[pos] = ' '; - memcpy(line + pos + 1, Word.data(), len); - pos += len + 1; + line += " " + word; } - line[pos] = '\0'; - SendText(std::string(line)); + SendText(linePrefix + line); } /* return 0 or 1 depending if users u and u2 share one or more common channels @@ -1357,22 +1051,19 @@ void User::SendText(const std::string &LinePrefix, std::stringstream &TextStream */ 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++) { /* 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; @@ -1383,83 +1074,14 @@ bool User::ChangeName(const char* gecos) 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; @@ -1472,106 +1094,30 @@ bool User::ChangeDisplayedHost(const char* shost) 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. @@ -1583,7 +1129,7 @@ void LocalUser::SetClass(const std::string &explicit_name) { 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()) { @@ -1593,7 +1139,7 @@ void LocalUser::SetClass(const std::string &explicit_name) 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; } } @@ -1603,7 +1149,7 @@ void LocalUser::SetClass(const std::string &explicit_name) for (ClassVector::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)); @@ -1611,7 +1157,7 @@ void LocalUser::SetClass(const std::string &explicit_name) 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; } @@ -1627,7 +1173,7 @@ void LocalUser::SetClass(const std::string &explicit_name) 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; } @@ -1637,7 +1183,7 @@ void LocalUser::SetClass(const std::string &explicit_name) */ 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; } @@ -1645,7 +1191,7 @@ void LocalUser::SetClass(const std::string &explicit_name) 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) @@ -1654,9 +1200,9 @@ void LocalUser::SetClass(const std::string &explicit_name) 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; } } @@ -1676,27 +1222,13 @@ void LocalUser::SetClass(const std::string &explicit_name) } } -/* 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 (UCListIter i = this->chans.begin(); i != this->chans.end(); ) { - Channel* c = *f; + Channel* c = (*i)->chan; + ++i; c->DelUser(this); } @@ -1707,20 +1239,21 @@ const std::string& FakeUser::GetFullHost() { 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(0), + limit(0), resolvehostnames(true) { } @@ -1730,7 +1263,7 @@ ConnectClass::ConnectClass(ConfigTag* tag, char t, const std::string& mask, cons 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) { } @@ -1753,4 +1286,5 @@ void ConnectClass::Update(const ConnectClass* src) maxconnwarn = src->maxconnwarn; maxchans = src->maxchans; limit = src->limit; + resolvehostnames = src->resolvehostnames; } diff --git a/src/version.sh b/src/version.sh index 7e11aaaea..d307082f4 100755 --- a/src/version.sh +++ b/src/version.sh @@ -1,2 +1,2 @@ #!/bin/sh -echo "InspIRCd-2.0.16" +echo "InspIRCd-2.2.0+pre" diff --git a/src/whois.cpp b/src/whois.cpp deleted file mode 100644 index bec9c7ea9..000000000 --- a/src/whois.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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()); -} - - - diff --git a/src/wildcard.cpp b/src/wildcard.cpp index eb9151293..b62fd8a61 100644 --- a/src/wildcard.cpp +++ b/src/wildcard.cpp @@ -19,15 +19,14 @@ */ -/* $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; @@ -74,46 +73,53 @@ static bool match_internal(const unsigned char *str, const 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; +} diff --git a/src/xline.cpp b/src/xline.cpp index 66d24f439..20693b599 100644 --- a/src/xline.cpp +++ b/src/xline.cpp @@ -158,7 +158,7 @@ void XLineManager::CheckELines() for (LocalUserList::const_iterator u2 = ServerInstance->Users->local_users.begin(); u2 != ServerInstance->Users->local_users.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; @@ -259,7 +259,7 @@ bool XLineManager::AddLine(XLine* line, User* user) 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 @@ -281,10 +281,10 @@ bool XLineManager::AddLine(XLine* line, User* user) 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; } @@ -308,7 +308,7 @@ bool XLineManager::DelLine(const char* hostmask, const std::string &type, User* ServerInstance->BanCache->RemoveEntries(y->second->type, true); - FOREACH_MOD(I_OnDelLine,OnDelLine(user, y->second)); + FOREACH_MOD(OnDelLine, (user, y->second)); y->second->Unset(); @@ -328,7 +328,7 @@ 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++) { - User* u = (User*)(*u2); + LocalUser* u = *u2; u->exempt = false; } @@ -409,7 +409,7 @@ XLine* XLineManager::MatchesLine(const std::string &type, const std::string &pat // 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(); @@ -430,10 +430,10 @@ void XLineManager::ExpireLine(ContainerIter container, LookupIter item) // 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()) + LocalUserList& list = ServerInstance->Users->local_users; + for (LocalUserList::iterator j = list.begin(); j != list.end(); ++j) { - User* u = *u2++; + LocalUser* u = *j; // Don't ban people who are exempt. if (u->exempt) @@ -471,7 +471,7 @@ void XLineManager::InvokeStats(const std::string &type, int numeric, User* user, 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; } @@ -531,30 +531,28 @@ bool XLine::IsBurstable() 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)) @@ -576,7 +574,8 @@ void KLine::Apply(User* u) 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)) @@ -598,7 +597,8 @@ void GLine::Apply(User* u) 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)) @@ -615,7 +615,8 @@ bool ELine::Matches(User *u) 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)) @@ -641,7 +642,7 @@ bool QLine::Matches(User *u) 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->ForceNickChange(u->uuid); } @@ -681,65 +682,42 @@ 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++) { - 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() diff --git a/tools/create_templates.pl b/tools/create_templates.pl deleted file mode 100755 index dfdaa29d1..000000000 --- a/tools/create_templates.pl +++ /dev/null @@ -1,87 +0,0 @@ -#!/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 CoreExport 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 CoreExport 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 " virtual ReturnType operator() (" . join(', ', @templatetypepairs[1..$#templatetypepairs]) . ")\n"; - print " {\n"; - print " return this->target->Call(" . join(', ', @lctemplatetypes[1..$#lctemplatetypes]) . ");\n"; - print " }\n"; - print "};\n\n"; -} - diff --git a/tools/genssl b/tools/genssl new file mode 100755 index 000000000..26f4f76c5 --- /dev/null +++ b/tools/genssl @@ -0,0 +1,135 @@ +#!/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 = !system "$certtool --version >/dev/null 2>&1"; +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 exit code of openssl/gnutls-certtool. +my $status = 0; + +if ($tool eq 'gnutls') { + 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 --outfile key.pem"; + $status ||= system "$certtool --generate-self-signed --load-privkey key.pem --outfile cert.pem --template $tmp"; + $status ||= system "$certtool --generate-dh-params --bits 2048 --outfile dhparams.pem"; +} 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'; +} + +if ($status) { + print STDERR "SSL generation failed: $tool exited with a non-zero status!\n"; + exit 1; +} diff --git a/tools/test-build b/tools/test-build new file mode 100755 index 000000000..7f1bf68cd --- /dev/null +++ b/tools/test-build @@ -0,0 +1,67 @@ +#!/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'; +} diff --git a/win/.gitignore b/win/.gitignore index f53ca0701..4b875ca61 100644 --- a/win/.gitignore +++ b/win/.gitignore @@ -1,3 +1,2 @@ -inspircd_version.h
-inspircd_config.h
+config.h
inspircd.rc
diff --git a/win/CMakeLists.txt b/win/CMakeLists.txt index e6a854d99..a9c58bcfc 100644 --- a/win/CMakeLists.txt +++ b/win/CMakeLists.txt @@ -12,6 +12,9 @@ set(EXTRA_LIBS "" CACHE PATH "Extra library paths") set(INSPIRCD_BASE "${CMAKE_CURRENT_SOURCE_DIR}/../") +# Build with multiple processes +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") + # Use our own NSIS template set(CMAKE_MODULE_PATH "${INSPIRCD_BASE}/win") @@ -56,8 +59,7 @@ if(MSVC) 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}/win/config.h.cmake" "${INSPIRCD_BASE}/win/config.h") add_executable(inspircd ${INSPIRCD_SOURCES} "${INSPIRCD_BASE}/win/inspircd.rc") target_link_libraries(inspircd win32_memory) diff --git a/win/inspircd_config.h.cmake b/win/config.h.cmake index f98cd391e..3a34a706b 100644 --- a/win/inspircd_config.h.cmake +++ b/win/config.h.cmake @@ -1,13 +1,15 @@ -#ifndef INSPIRCD_CONFIG_H
-#define INSPIRCD_CONFIG_H
+#pragma once
+
+#define BRANCH "@MAJOR_VERSION@.@MINOR_VERSION@"
+#define VERSION "@FULL_VERSION@"
+#define REVISION "release"
+#define SYSTEM "@CMAKE_SYSTEM@"
+#define INSPIRCD_SOCKETENGINE_NAME "select"
#define CONFIG_PATH "@CONF_PATH@"
#define MOD_PATH "@MODULE_PATH@"
#define DATA_PATH "@DATA_PATH@"
#define LOG_PATH "@LOG_PATH@"
-#define MAXBUF 514
#include "inspircd_win32wrapper.h"
#include "threadengines/threadengine_win32.h"
-
-#endif
\ No newline at end of file diff --git a/win/inspircd_memory_functions.cpp b/win/inspircd_memory_functions.cpp index 398708317..2093bf3a8 100644 --- a/win/inspircd_memory_functions.cpp +++ b/win/inspircd_memory_functions.cpp @@ -16,6 +16,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + #include <windows.h> #include <exception> #include <new> diff --git a/win/inspircd_version.h.cmake b/win/inspircd_version.h.cmake deleted file mode 100644 index 1aec12b24..000000000 --- a/win/inspircd_version.h.cmake +++ /dev/null @@ -1,4 +0,0 @@ -#define BRANCH "@MAJOR_VERSION@.@MINOR_VERSION@"
-#define VERSION "@FULL_VERSION@"
-#define REVISION "0"
-#define SYSTEM "@CMAKE_SYSTEM@"
\ No newline at end of file diff --git a/win/inspircd_win32wrapper.cpp b/win/inspircd_win32wrapper.cpp index 048baf38b..eba18dca3 100644 --- a/win/inspircd_win32wrapper.cpp +++ b/win/inspircd_win32wrapper.cpp @@ -22,6 +22,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + #include "inspircd_win32wrapper.h" #include "inspircd.h" #include "configreader.h" @@ -85,7 +86,7 @@ CoreExport int insp_inet_pton(int af, const char *src, void *dst) } return 1; } - + return 0; } @@ -163,7 +164,7 @@ int getopt_long(int ___argc, char *const *___argv, const char *__shortopts, cons // optind++; // Trash this next argument, we won't be needing it. par = ___argv[optind-1]; } - } + } // increment the argument for next time // optind++; @@ -189,9 +190,9 @@ int getopt_long(int ___argc, char *const *___argv, const char *__shortopts, cons { if (__longopts[i].val == -1 || par == 0) return 1; - + return __longopts[i].val; - } + } break; } } diff --git a/win/inspircd_win32wrapper.h b/win/inspircd_win32wrapper.h index 75404ef8b..9e6724126 100644 --- a/win/inspircd_win32wrapper.h +++ b/win/inspircd_win32wrapper.h @@ -19,18 +19,16 @@ */ +#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 @@ -45,9 +43,6 @@ #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 @@ -73,6 +68,17 @@ #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 @@ -92,6 +98,10 @@ CoreExport const char * insp_inet_ntop(int af, const void * src, char * dst, soc #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); } @@ -99,9 +109,12 @@ __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; }; @@ -201,5 +214,5 @@ private: DWORD dwErrorCode; }; -#endif - +// Same value as EXIT_STATUS_FORK (EXIT_STATUS_FORK is unused on Windows) +#define EXIT_STATUS_SERVICE 4 diff --git a/win/modules/CMakeLists.txt b/win/modules/CMakeLists.txt index 5778d944a..b3e59de61 100644 --- a/win/modules/CMakeLists.txt +++ b/win/modules/CMakeLists.txt @@ -2,33 +2,41 @@ # so copy the file out of extra/
file(COPY "${INSPIRCD_BASE}/src/modules/extra/m_regex_stdlib.cpp" DESTINATION "${INSPIRCD_BASE}/src/modules/")
-file(GLOB INSPIRCD_MODULES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "${INSPIRCD_BASE}/src/commands/*.cpp" "${INSPIRCD_BASE}/src/modules/*.cpp")
+file(GLOB INSPIRCD_MODULES "${INSPIRCD_BASE}/src/coremods/core_*" "${INSPIRCD_BASE}/src/modules/m_*")
list(SORT INSPIRCD_MODULES)
add_definitions("-DDLL_BUILD")
foreach(MODULE_NAME ${INSPIRCD_MODULES})
- string(REGEX REPLACE "^.*[/\\](.*).cpp$" "\\1.so" SO_NAME ${MODULE_NAME})
- add_library(${SO_NAME} MODULE ${MODULE_NAME})
- set_target_properties(${SO_NAME} PROPERTIES PREFIX "" SUFFIX "")
+ if(IS_DIRECTORY "${MODULE_NAME}")
+ string(REGEX REPLACE "^.*[/\\](.*)$" "\\1" BASE_NAME ${MODULE_NAME})
+ else(IS_DIRECTORY "${MODULE_NAME}")
+ string(REGEX REPLACE "^.*[/\\](.*).cpp$" "\\1" BASE_NAME ${MODULE_NAME})
+ endif(IS_DIRECTORY "${MODULE_NAME}")
+ set(SO_NAME "${BASE_NAME}.so")
+
+ if(IS_DIRECTORY "${MODULE_NAME}")
+ file(GLOB MODULES_SUBDIR_SRCS "${MODULE_NAME}/*.cpp")
+ list(SORT MODULES_SUBDIR_SRCS)
+ add_library(${SO_NAME} MODULE ${MODULES_SUBDIR_SRCS})
+ else(IS_DIRECTORY "${MODULE_NAME}")
+ add_library(${SO_NAME} MODULE ${MODULE_NAME})
+ endif(IS_DIRECTORY "${MODULE_NAME}")
+
+ # Generate the module and set its linker flags, also set it to depend on the main executable to be built beforehand
target_link_libraries(${SO_NAME} inspircd)
add_dependencies(${SO_NAME} inspircd)
if(MSVC)
target_link_libraries(${SO_NAME} win32_memory)
add_dependencies(${SO_NAME} win32_memory)
endif(MSVC)
- install(TARGETS ${SO_NAME} DESTINATION modules)
-endforeach(MODULE_NAME ${INSPIRCD_MODULES})
-file(GLOB INSPIRCD_MODULES_SPANNINGTREE RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "${INSPIRCD_BASE}/src/modules/m_spanningtree/*.cpp")
-list(SORT INSPIRCD_MODULES_SPANNINGTREE)
+ set_target_properties(${SO_NAME} PROPERTIES
+ PREFIX ""
+ SUFFIX ""
+ COMPILE_DEFINITIONS "MODNAME=\"${BASE_NAME}\""
+ )
-add_library(m_spanningtree.so MODULE ${INSPIRCD_MODULES_SPANNINGTREE})
-set_target_properties(m_spanningtree.so PROPERTIES PREFIX "" SUFFIX "")
-target_link_libraries(m_spanningtree.so inspircd)
-add_dependencies(m_spanningtree.so inspircd)
-if(MSVC)
- target_link_libraries(m_spanningtree.so win32_memory)
- add_dependencies(m_spanningtree.so win32_memory)
-endif(MSVC)
-install(TARGETS m_spanningtree.so DESTINATION modules)
\ No newline at end of file + # Set the module to be installed to the module directory
+ install(TARGETS ${SO_NAME} DESTINATION modules)
+endforeach(MODULE_NAME ${INSPIRCD_MODULES})
diff --git a/win/win32service.cpp b/win/win32service.cpp index c34e9957d..448829a1d 100644 --- a/win/win32service.cpp +++ b/win/win32service.cpp @@ -17,7 +17,7 @@ */ -#include "inspircd_config.h" +#include "config.h" #include "inspircd.h" #include "exitcodes.h" #include <windows.h> @@ -280,7 +280,7 @@ int main(int argc, char* argv[]) } else { - return EXIT_STATUS_INTERNAL; + return EXIT_STATUS_SERVICE; } } return 0; diff --git a/win/win32service.h b/win/win32service.h index e4500be13..d8177eabc 100644 --- a/win/win32service.h +++ b/win/win32service.h @@ -15,7 +15,9 @@ * 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 */ |