SurrealServices has the following copyrights:
-Copyright tabris@surrealchat.net (tabris@tabris.net) 2004, 2005, 2006, 2007, 2008
-Copyright saturn@surrealchat.net 2004, 2005, 2006, 2007, 2008
+Copyright tabris@surrealchat.net (tabris@tabris.net) 2004, 2005, 2006, 2007, 2008, 2009, 2010
+Copyright saturn@surrealchat.net 2004, 2005, 2006, 2007, 2008, 2009
Copyright errietta@hotmail.com 2008
+Copyright musashix90@gmail.com 2009, 2010
We do not claim ownership of the following modules which we have
imported into our tree:
* Perl 5.8 and standard modules. (threads no longer required)
NOTE: Many linux distros place Perl's standard modules in a separate
package from Perl itself.
-* MySQL 4.1 or 5.0. 5.1 and up are not supported.
+* MySQL 5.0 or 5.1. 5.2 is not well tested; 5.3 and up are not
+supported.
* Event module from CPAN
* Date::Parse module from CPAN
Both the Event and Date::Parse modules should be available
necessary aspect of a properly maintained network, and as such
should not be an undue burden.
+ Definition of 'unsyncserver': This means a server that is
+not 100% conformant to the UnrealIRCd Server Protocol. Basically
+most services servers (NeoStats, denora, janus, etc) don't send an
+EOS at the end of their netburst. It seems that Unreal is perfectly
+happy with this, but SrSv isn't. When we don't receive an
+End-of-Sync message, we don't know if they're done announcing
+everything, and thus whether to start re-mangling channel modes.
+
Further of note is that SrSv does not have a full
capability list for ircds and such may be necessary for
portability (if your ircd does not support things like WATCH,
SILENCE, etc).
IRC Networks known to be using SrSv as of this date
-(20060904):
- irc.talkingirc.net
+(20100506):
irc.surrealchat.net (duh)
- irc.webchatting.com
+ irc.CrystalNET.eu
+ irc.lucidchat.net
+ irc.pokebeach.com
We would appreciate any success reports from other networks,
contact us on irc.surrealchat.net #dev.lounge or via email:
--- /dev/null
+ SQLserv is a bot intended to make direct query of the database
+possible. It is not considered 'stable', and it barely works at all
+right now.
+
+ First, this service is potentially dangerous. At present only
+read-only commands are possible, but it is capable of being extended to
+allow modification of the database. Doing so without knowledge of the
+workings of the program may BREAK the program. If you do so you get to
+keep all the pieces. The coders of this module cannot be held
+responsible for what you do with it.
+
+ Second, at present it requires the 'services' module to be
+loaded, and the user to have ROOT access. This is for your protection.
+Modifying this module to allow regular opers to use this module MAY
+BREAK the app, and/or expose them to information that they are otherwise
+not supposed to have. Again, the coders of this module cannot be held
+responsible for what you do with it.
+
+ Third, this module does not protect you from doing invalid
+queries. This module does not prevent you from doing queries that may
+take 5 minutes to complete. Since the module has to run everything in
+the parent process, this may BREAK YOUR APP. As usual, we are not
+responsible for what you do with it.
+ADDENDUM: SQL queries are no longer executed in the parent, but the
+disclaimer still applies.
+
+ Fourth, there is no documentation for this module, not that much
+is necessary. You submit SQL queries to it, as if you were using the
+MySQL shell. It attempts to present the result back to you, much as the
+MySQL shell would. Embedded newlines in the returned data MAY BREAK. Not
+that there should be many cases of this in this program. You cannot run
+dependent queries (LOCK first, then SELECT, then UNLOCK), you cannot
+instantiate TEMPORARY tables. You cannot start a transaction. One-shot
+queries are all that is safe.
--- /dev/null
+ SpamServ is a module written to watch channels for on-join private
+messages, which might be an indication of a spam bot. This module is not
+considered to be stable, and does not take any action for private messages
+received, only reports the private messages to the diagnostics channel.
+
+ This service requires the 'services' module to be loaded, as well
+as a population of nicknames in the 'config/spamserv/nicklist.txt' directory.
+The module itself assumes that there are nicknames supplied in the .txt file,
+delimited by a new line.
+
+ There is some documentation of this module, in the form of
+/MSG SpamServ HELP
--- /dev/null
+# This file is part of SurrealServices.
+#
+# SurrealServices is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# SurrealServices is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with SurrealServices; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package SrSv::64bit;
+
+use strict;
+use Exporter qw( import );
+BEGIN {
+ require Config;
+
+ require constant;
+ import constant { HAS_64BIT_INT => ($Config::Config{use64bitint} eq 'define'), };
+ our @EXPORT = qw( HAS_64BIT_INT );
+}
+
+1;
use SrSv::Conf2Consts qw(main);
use SrSv::Debug;
-use SrSv::Unreal::Tokens;
+use SrSv::Unreal::Tokens qw( :tokens );
use SrSv::Unreal::Base64 qw(itob64);
use SrSv::IRCd::State qw(synced $ircd_ready %IRCd_capabilities);
use SrSv::IRCd::IO qw(ircsend ircsendimm);
$agents{lc $nick}{PARMS} = [ @_ ];
$host = main_conf_local unless $host;
- ircsend($tkn{NICK}[$tkn]." $nick 1 $time $ident $host ".
+ ircsend("@{[TOK_NICK]} $nick 1 $time $ident $host ".
(SJB64 ? itob64(main_conf_numeric) : main_conf_local).
" 1 $modes * :$gecos");
foreach my $chan (@chans) {
- ircsend(":$nick ".$tkn{JOIN}[$tkn]." $chan");
+ ircsend(":$nick @{[TOK_JOIN]} $chan");
# If we tracked chanmodes for agents, that would go here as well.
}
}
delete($agents{lc $nick}{CHANS});
delete($agents{lc $nick});
- ircsendimm(":$nick ".$tkn{QUIT}[$tkn]." :$msg");
+ ircsendimm(":$nick @{[TOK_QUIT]} :$msg");
}
sub agent_quit_all($) {
if($agents{lc $agent}) {
$agents{lc $agent}{CHANS}{lc $chan} = 1;
- ircsend(":$agent ".$tkn{JOIN}[$tkn]." $chan");
+ ircsend(":$agent @{[TOK_JOIN]} $chan");
} else {
if($ircd_ready) {
print "Tried to make nonexistent agent ($agent) join channel ($chan)" if DEBUG;
my ($agent, $chan, $reason) = @_;
delete($agents{lc $agent}{CHANS}{lc $chan});
- ircsend(":$agent $tkn{PART}[$tkn] $chan :$reason");
+ ircsend(":$agent @{[TOK_PART]} $chan :$reason");
}
sub set_agent_umode($$) {
my ($src, $modes) = @_;
- ircsend(":$src $tkn{UMODE2}[$tkn] $modes");
+ ircsend(":$src @{[TOK_UMODE2]} $modes");
}
sub agent_sync() {
if($src =~ /\./) {
# let's NOT loopback this event
- ircsendimm(':'.main_conf_local.' '.$tkn{KILL}[$tkn]." $dst :Nick Collision");
+ ircsendimm(':'.main_conf_local.' '."@{[TOK_KILL]} $dst :Nick Collision");
} elsif (defined($agents{lc $src})) {
# Do Nothing.
} else {
CRF_AUTOVOICE => 1024,
CRF_WELCOMEINCHAN => 2048,
CRF_NEVEROP => 4096,
+ CRF_NOCLONES => 8192,
);
our @EXPORT = (qw(cr_chk_flag cr_set_flag), keys(%constants));
--- /dev/null
+# This file is part of SurrealServices.
+#
+# SurrealServices is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# SurrealServices is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with SurrealServices; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package SrSv::Conf::main;
+
+use SrSv::Conf::Parameters main => [
+ qw(local remote port numeric pass load email replyto),
+ [info => 'SurrealServices'],
+ [procs => 4],
+ [diag => '#Diagnostics'],
+ [netname => 'Network'],
+ [sig => 'Thank you for chatting with us.'],
+ [unsyncserver => undef],
+ [nomail => undef],
+ [logmail => undef],
+ [hashed_passwords => undef],
+ [ban_webchat_prefixes => 'java|htIRC'],
+ [ipv6 => 0], # not enabled by default as not all systems support it
+ [tokens => 1], # turn off for debugging, so debug-output is easier to read
+ [queue_lowwater => 30],
+ [queue_highwater => 50],
+ [operchan => undef],
+];
+
+1;
--- /dev/null
+# This file is part of SurrealServices.
+#
+# SurrealServices is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# SurrealServices is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with SurrealServices; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package SrSv::Conf::services;
+
+use SrSv::Conf::Parameters services => [
+ [noexpire => undef],
+ [nickexpire => 21],
+ [vacationexpire => 90],
+ [nearexpire => 7],
+ [chanexpire => 21],
+ [validate_email => undef],
+ [validate_expire => 1],
+ [clone_limit => 3],
+ [chankilltime => 86400],
+
+ [default_protect => 'normal'],
+ [default_chanbot => undef],
+ [default_channel_mlock => '+nrt'],
+ [old_user_age => 300],
+ [chanreg_needs_oper => 0],
+
+ [log_overrides => 0],
+
+ [botserv => undef],
+ [nickserv => undef],
+ [chanserv => undef],
+ [memoserv => undef],
+ [adminserv => undef],
+ [operserv => undef],
+ [hostserv => undef],
+
+];
+
+1;
--- /dev/null
+# This file is part of SurrealServices.
+#
+# SurrealServices is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# SurrealServices is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with SurrealServices; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package SrSv::Conf::sql;
+
+use SrSv::Conf::Parameters sql => [
+ qw(mysql_user mysql_pass mysql_db),
+ [server_prepare => 0],
+];
+
+1;
die "Unable to find a suitable SHA implementation\n";
}
}
+use Digest::MD5;
=item Hash Notes
return $string;
}
+sub makeHash_vBulletin($;$$$) {
+ my ($secret, $salt, $algorithm, $version) = @_;
+ $algorithm = 'md5' unless $algorithm;
+ $salt = makeBinSalt(3) unless $salt;
+ $version = 2 unless $version;
+ my $string = "$algorithm:$version:";
+ $string .= encode_base64($salt, '').':';
+ $string .= md5_base64(md5_hex($secret) . $salt);
+ return $string;
+}
+
sub __makeHash($$) {
my ($plaintext, $algorithm) = @_;
$algorithm = 'sha256';
my $hash2;
if($version eq 'v0') {
$hash2 = makeHash_v0($plain, $salt, $algorithm);
+ } elsif($version eq 'vBulletin') {
+ $hash2 = makeHash_vBulletin($plain, $salt, $algorithm);
} else {
$hash2 = makeHash_v1($plain, $salt, $algorithm, $version);
}
our java iframe is _not_ open source [yet]. I do not know if it will be either.
=cut
+use SrSv::Conf2Consts qw( main );
+our $ident_regexp = qr/^(@{[main_conf_ban_webchat_prefixes]})-/;
sub make_hostmask($$$$) {
my ($type, $nick, $ident, $host) = @_;
no warnings 'prototype'; #we call ourselves
if($type == 10) {
- if ($ident =~ /^java-/) {
+ if ($ident =~ $ident_regexp) {
return make_hostmask(3, $nick, $ident, $host);
}
else {
--- /dev/null
+package SrSv::IPv6;
+
+use Exporter qw( import );
+use SrSv::Conf2Consts qw( main );
+
+use SrSv::64bit;
+BEGIN {
+ our @EXPORT = qw( is_ipv6 get_ipv6_net get_ipv6_64 );
+ if(main_conf_ipv6) {
+ require Socket; import Socket;
+ require Socket6; import Socket6;
+ if(!HAS_64BIT_INT) {
+ eval {
+ require Math::BigInt;
+ import Math::BigInt try => 'GMP';
+ };
+ if($@) {
+ print STDERR "Running old version of perl/Math::BigInt.\n", $@, "Trying again.\n";
+ require Math::BigInt;
+ import Math::BigInt;
+ }
+ }
+ push @EXPORT, qw( AF_INET6 );
+ }
+}
+
+sub is_ipv6($) {
+ my ($addr) = @_;
+ if($addr =~ /^((?:\d{1,3}\.){3}\d{1,3})$/) {
+ return 0 unless wantarray;
+ return (0, $addr);
+ }
+ elsif($addr =~ /:ffff:((?:\d{1,3}\.){3}\d{1,3})$/) {
+ return 0 unless wantarray;
+ return (0, $1);
+ } else {
+ return 1 unless wantarray;
+ return (1, $addr);
+ }
+}
+
+
+sub get_ipv6_net($) {
+# grabs the top 64bits of the IPv6 addr.
+ my ($addr) = @_;
+ my $str = Socket6::inet_pton(AF_INET6, $addr);
+ my (@words) = unpack('H4H4H4H4H4H4H4H4', $str);
+ my $int = ( !HAS_64BIT_INT ? Math::BigInt->bzero() : 0 );
+ for(0..3) {
+ $int <<= 16;
+ $int |= hex($words[$_]);
+ }
+ return $int;
+}
+
+sub get_ipv6_64($) {
+ my ($addr) = @_;
+ my $str = Socket6::inet_pton(AF_INET6, $addr);
+ return join(":", unpack("H4H4H4H4", $str))."::/64";
+}
+
+1;
use SrSv::Debug;
+use SrSv::Conf2Consts qw( main );
+
use SrSv::IRCd::Queue qw(ircd_enqueue);
use SrSv::IRCd::State qw($ircline $ircline_real synced initial_synced);
use SrSv::Message qw(add_callback message);
-# FIXME
-use constant {
- # Wait For
- WF_NONE => 0,
- WF_NICK => 1,
- WF_CHAN => 2,
- WF_ALL => 3,
-};
+use SrSv::Constants;
sub addhandler($$$$;$) {
my ($type, $src, $dst, $cb, $po) = @_;
});
}
+our $last_highqueue = time();
sub callfuncs {
my ($args, $sync, $wf, $message);
ARGS => $args,
ON_FINISH => ($sync ? undef : 'SrSv::IRCd::Queue::finished'), # FIXME
SYNCED => [synced, initial_synced],
+ QUEUE_DEPTH_HIGHPRIO => SrSv::IRCd::Queue::queue_size(WF_ALL),
+ QUEUE_DEPTH => SrSv::IRCd::Queue::queue_size(WF_MAX), # but not WF_MSG
};
+ if(initial_synced && ($message->{QUEUE_DEPTH_HIGHPRIO} > main_conf_queue_lowwater) && ($last_highqueue < time()-5)) {
+ ircd::privmsg_noloop(main_conf_local, main_conf_operchan, "HIGH TRAFFIC WARNING",
+ "Queue depth exceeded @{[main_conf_queue_lowwater]}") if defined(main_conf_operchan);
+ ircd::privmsg_noloop(main_conf_local, main_conf_diag, "HIGH TRAFFIC WARNING",
+ "Queue depth exceeded @{[main_conf_queue_lowwater]}");
+ $last_highqueue = time();
+ }
if($sync) {
message($message);
my ($message, $callback) = @_;
print "Calling ", $callback->{REALCALL}, " ", join(',', @{$message->{ARGS}}), "\n" if DEBUG();
- $ircline = $message->{IRCLINE};
+ local $ircline = $message->{IRCLINE};
local $SrSv::IRCd::State::synced = $message->{SYNCED}[0]; # XXX This is questionable.
local $SrSv::IRCd::State::initial_synced = $message->{SYNCED}[1];
+ local $SrSv::IRCd::State::queue_depth = $message->{QUEUE_DEPTH};
print "IRCLINE is $ircline synced is $SrSv::IRCd::State::synced initial_synced is $SrSv::IRCd::State::initial_synced\n" if DEBUG();
use SrSv::Debug;
use SrSv::IRCd::State qw($ircline $ircline_real $ircd_ready);
use SrSv::IRCd::Event qw(callfuncs);
-use SrSv::Unreal::Tokens;
+use SrSv::Unreal::Tokens qw( :tokens );
use SrSv::IRCd::Parse qw(parse_line);
use SrSv::RunLevel qw(emerg_shutdown);
use SrSv::Log qw( write_log );
ircsendimm(@_);
} else {
foreach my $x (@_) {
- if($x =~ /^$tkn{NICK}[$tkn]/) {
+ if($x =~ /^@{[TOK_NICK]}/) {
unshift @queue, $x;
} else {
push @queue, $x;
use SrSv::Debug;
use SrSv::Message qw(message);
+use SrSv::Constants qw( WF_MAX );
-our @queue = map [], 0..3; # 3 is the maximum WF value
+our @queue = map [], 0..WF_MAX;
sub ircd_enqueue($) {
my ($message) = @_;
}
}
-sub queue_size() {
+sub queue_size(;$) {
+ my ($depth) = @_;
+ if(!$depth) {
+ $depth = WF_MAX;
+ }
my $r;
- foreach (@queue) { $r += @$_ }
+ for(my $i = 0; $i < $depth; ++$i) {
+ $r += scalar @{$queue[$i]};
+ }
return $r;
}
our @EXPORT_OK = qw($ircline $ircline_real $remoteserv $ircd_ready synced initial_synced create_server get_server_children set_server_state set_server_juped get_server_state get_online_servers %IRCd_capabilities);
# FIXME - synced() is called very often and should be cached locally
-use SrSv::Process::InParent qw(calc_synced create_server get_server_children set_server_state set_server_juped get_server_state get_online_servers);
+use SrSv::Process::InParent qw(
+ calc_synced
+ __initial_synced_inparent __synced_inparent
+ create_server get_server_children
+ set_server_state set_server_juped
+ get_server_state get_online_servers);
use SrSv::Conf 'main';
our %juped_servers;
our $synced;
our $initial_synced;
+our $queue_depth;
-sub synced {
+sub __initial_synced_inparent {
+ return $initial_synced;
+}
+sub __synced_inparent {
return $synced;
}
+sub synced {
+# $ircline is zero if running in a timer context (among other possibilities)
+ return ($ircline ? $synced : __synced_inparent());
+}
+
sub initial_synced {
- return $initial_synced;
+ return ($ircline ? $initial_synced : __synced_inparent());
}
sub calc_synced {
PARENT => $parent,
CHILDREN => [],
SYNCED => 0,
+ NONCONFORMANT => isNonconformant($parent, $child),
};
push @{$servers{$parent}{CHILDREN}}, $child if $parent;
$juped_servers{$server} = 1;
}
+sub isNonconformant(@) {
+ my (@serverList) = @_;
+ foreach my $server (@serverList) {
+ if(defined($servers{$server}) && $servers{$server}->{NONCONFORMANT}) {
+ return 1;
+ }
+ if(defined $main_conf{'unsyncserver'}) {
+ my @list;
+ if(ref($main_conf{'unsyncserver'}) eq 'ARRAY') {
+ @list = @{$main_conf{'unsyncserver'}};
+ } else {
+ @list = ($main_conf{'unsyncserver'});
+ }
+ if(grep (m/^$server$/i, @list) ) {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
sub get_server_state {
my ($server) = @_;
- my $badserver = $main_conf{'unsyncserver'};
- return 1 if($badserver and lc $server eq lc $badserver); # I HATE NEOSTATS
+ return 1 if isNonconformant($server);
return $servers{$server}{SYNCED};
}
--- /dev/null
+#!/usr/bin/perl
+
+
+# This file is part of SurrealServices.
+#
+# SurrealServices is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 2,
+# as published by the Free Software Foundation.
+#
+# SurrealServices is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with SurrealServices; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+=cut
+
+THIS CODE IS alpha only, and untested. Don't just trust it blindly.
+
+=cut
+
+use strict;
+use warnings;
+
+sub isAlpha($) {
+ my ($char) = @_;
+ return ($char =~ /[A-Z]/);
+}
+
+sub getBase36($) {
+ my ($char) = @_;
+ if(isAlpha($char)) {
+ my $val = (ord($char) - ord('A')) + 10;
+ #print "$val\n";
+ return $val;
+ } else {
+ return int($char);
+ }
+}
+
+sub decodeUUID($) {
+ my ($UUID) = @_;
+ my @chars = split(//, $UUID);
+ my @sidC = @chars[0..2];
+ my @uidC = @chars[3..8];
+ my $sidN = int($sidC[0]) << (4 + (6 * 2));
+ $sidN |= getBase36($sidC[1]) << (4 + (6 * 1));
+ $sidN |= getBase36($sidC[2]) << (4 + (6 * 0));
+ my $uidN = 0;
+ foreach my $char (@uidC) {
+ #print "$char\n";
+ $uidN <<= 6;
+ $uidN |= getBase36($char);
+ }
+ return (($sidN << 48) | $uidN);
+}
+
+my $int = decodeUUID('751AAAAAA');
+print "$int\n";
+print log($int)/log(2), "\n";
use strict;
use IO::Handle;
+use English qw(-no_match_var);
use SrSv::Debug;
use SrSv::Timer qw(add_timer);
use SrSv::Time;
use SrSv::Process::InParent qw(write_log open_log close_log rotate_logs close_all_logs);
+use IO::File;
use SrSv::Text::Codes qw( strip_codes );
}
my ($year, $month, undef, $mday) = gmt_date();
my $filename2 = $filename.'-'.sprintf('%04d-%02d-%02d', $year, $month, $mday);
-
- open $log_handles{lc $handle}, '>>', $path.'/'.$filename2;
+
+ my $fh;
+ if($fh = IO::File->new($path.'/'.$filename2, '>>')) {
+ } else {
+ use SrSv::RunLevel qw( main_shutdown );
+ ircd::debug_nolog(qq(Unable to open "$path/$filename2": $OS_ERROR}));
+ main_shutdown();
+ }
+ $fh->autoflush(1);
+ $log_handles{lc $handle} = $fh;
$file_handles{lc $handle} = { BASENAME => $filename, FILENAME => $filename2 };
- $log_handles{lc $handle}->autoflush(1);
}
sub close_log($) {
ircd::debug_nolog("undefined log-handle $handle, aborting close()");
return undef;
}
- close $log_handles{lc $handle};
+ $log_handles{lc $handle}->close();
+ delete($log_handles{lc $handle});
delete($log_handles{lc $handle});
}
sub rotate_logs() {
foreach my $handle (keys(%file_handles)) {
- close $log_handles{$handle};
+ $log_handles{lc $handle}->close();
my ($year, $month, undef, $mday) = gmt_date();
- $file_handles{lc $handle}{FILENAME} = $file_handles{lc $handle}{BASENAME}.'-'.sprintf('%04d-%02d-%02d', $year, $month, $mday);
- open $log_handles{$handle}, '>>', $path.'/'.$file_handles{lc $handle}{FILENAME};
+ $file_handles{lc $handle}{FILENAME} =
+ $file_handles{lc $handle}{BASENAME}.'-'.sprintf('%04d-%02d-%02d', $year, $month, $mday);
+ my $new_fh;
+ if($new_fh = IO::File->new($path.'/'.$file_handles{lc $handle}{FILENAME}, '>>')) {
+ } else {
+ use SrSv::RunLevel qw( main_shutdown );
+ my $new_path = "$path/".$file_handles{lc $handle}{FILENAME};
+ ircd::debug_nolog(qq(Unable to open "$new_path": $OS_ERROR}));
+ main_shutdown();
+ }
+ $log_handles{lc $handle} = $new_fh;
}
-
- add_timer('', get_nextday_time()-time(), __PACKAGE__, 'SrSv::Log::rotate_logs');
+
+ #add_timer('', get_nextday_time()-time(), __PACKAGE__, 'SrSv::Log::rotate_logs');
+ Event->timer( at => get_nextday_time(), cb => \&SrSv::Log::rotate_logs );
}
sub close_all_logs() {
foreach my $handle (keys(%file_handles)) {
- close $log_handles{$handle};
- $file_handles{lc $handle} = undef;
+ close_log($handle);
}
}
# set a timer to rotate logs on day-change
-add_timer('', get_nextday_time()-time(), __PACKAGE__, 'SrSv::Log::rotate_logs');
+Event->timer( at => get_nextday_time(), cb => \&SrSv::Log::rotate_logs );
1;
use strict;
-use Exporter 'import';
-BEGIN { our @EXPORT_OK = qw($dbh) }
+use DBI qw( :sql_types );
-use DBI;
+use Exporter 'import';
+BEGIN {
+ our @EXPORT_OK = (qw( $dbh connectDB disconnectDB ), @{$DBI::EXPORT_TAGS{'sql_types'}} );
+ our %EXPORT_TAGS = ( sql_types => $DBI::EXPORT_TAGS{'sql_types'} );
+}
use SrSv::Process::Init;
-use SrSv::Conf::Parameters sql => [
- qw(mysql_user mysql_pass mysql_db),
- [server_prepare => 0],
-];
+use SrSv::Conf::sql;
use SrSv::Conf 'sql';
our $dbh;
proc_init {
+ connectDB();
+};
+sub connectDB() {
$dbh = DBI->connect(
"DBI:mysql:".$sql_conf{'mysql-db'}.($sql_conf{server_prepare} ? ":mysql_server_prepare=1" : ''),
$sql_conf{'mysql-user'},
);
# Prevent timeout
$dbh->do("SET wait_timeout=(86400*365)");
-};
+}
+
+sub disconnectDB() {
+ $dbh->disconnect();
+ $dbh = undef;
+}
1;
use Carp qw( confess );
use SrSv::Debug;
-use SrSv::MySQL '$dbh';
+use SrSv::MySQL qw( $dbh :sql_types );
use SrSv::Process::Init;
-use DBI qw(:sql_types);
our %types;
--- /dev/null
+# This file is part of SurrealServices.
+#
+# SurrealServices is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# SurrealServices is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with SurrealServices; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package SrSv::NickReg::NickText;
+
+use strict;
+
+use Exporter 'import';
+
+BEGIN {
+ my %constants = (
+ NTF_QUIT => 1,
+ NTF_GREET => 2,
+ NTF_JOIN => 3,
+ NTF_AUTH => 4,
+ NTF_UMODE => 5,
+ NTF_VACATION => 6,
+ NTF_AUTHCODE => 7,
+ NTF_PROFILE => 8,
+ NTF_VHOST_REQ => 9,
+ );
+ require constant; import constant \%constants;
+ our @EXPORT = keys(%constants);
+}
+
+1;
use strict;
use Exporter 'import';
-BEGIN { our @EXPORT_OK = qw(is_identified chk_identified get_id_nicks get_nick_user_nicks get_nick_users) }
+BEGIN {
+ our @EXPORT_OK = qw(
+ is_identified chk_identified
+ get_id_nicks
+ get_nick_user_nicks get_nick_users get_nick_users_all
+ );
+}
use SrSv::Process::Init;
use SrSv::MySQL '$dbh';
use SrSv::NickReg::Flags;
use SrSv::Errors;
-our ($is_identified, $get_id_nicks, $get_nick_users);
-
-proc_init {
- $is_identified = $dbh->prepare("SELECT 1 FROM user, nickid, nickalias WHERE user.id=nickid.id AND user.nick=? AND nickid.nrid=nickalias.nrid AND nickalias.alias=?");
- $get_id_nicks = $dbh->prepare("SELECT nickreg.nick FROM nickid, nickreg WHERE nickid.nrid=nickreg.id AND nickid.id=?");
- $get_nick_users = $dbh->prepare("SELECT user.nick, user.id FROM user, nickid, nickalias WHERE user.id=nickid.id AND nickid.nrid=nickalias.nrid AND nickalias.alias=? AND user.online=1");
+my $find_user_tables = 'user JOIN nickid ON (user.id=nickid.id) JOIN nickalias ON (nickid.nrid=nickalias.nrid)';
+require SrSv::MySQL::Stub;
+import SrSv::MySQL::Stub {
+ __get_nick_users => ['ARRAY', "SELECT user.nick, user.id
+ FROM $find_user_tables WHERE nickalias.alias=? AND user.online=1"],
+ __get_nick_users_all => ['ARRAY', "SELECT user.nick, user.id, user.online
+ FROM $find_user_tables WHERE nickalias.alias=?"],
+ __is_identified => ['SCALAR', "SELECT 1
+ FROM $find_user_tables WHERE user.nick=? AND nickalias.alias=?"],
+ __get_id_nicks => ['COLUMN', "SELECT nickreg.nick
+ FROM nickid JOIN nickreg ON (nickid.nrid=nickreg.id) WHERE nickid.id=?"],
};
sub is_identified($$) {
my ($user, $rnick) = @_;
my $nick = get_user_nick($user);
-
- $is_identified->execute($nick, $rnick);
- return scalar $is_identified->fetchrow_array;
+
+ return __is_identified($nick, $rnick) ? 1 : 0;
}
sub chk_identified($;$) {
my ($user, $nick) = @_;
$nick = get_user_nick($user) unless $nick;
-
+
nickserv::chk_registered($user, $nick) or return 0;
unless(is_identified($user, $nick)) {
notice($user, $err_deny);
return 0;
}
-
+
return 1;
}
sub get_id_nicks($) {
my ($user) = @_;
my $id = get_user_id($user);
- my @nicks;
-
- $get_id_nicks->execute($id);
- my $ref = $get_id_nicks->fetchall_arrayref;
- return map $_->[0], @$ref;
+ return __get_id_nicks($id);
}
sub get_nick_user_nicks($) {
my ($nick) = @_;
- $get_nick_users->execute($nick);
- my $ref = $get_nick_users->fetchall_arrayref;
-
- return map $_->[0], @$ref;
+ return map $_->[0], __get_nick_users($nick);
}
sub get_nick_users($) {
my ($nick) = @_;
- $get_nick_users->execute($nick);
- my $ref = $get_nick_users->fetchall_arrayref;
-
- return map +{NICK => $_->[0], ID => $_->[1]}, @$ref;
+ return map +{ NICK => $_->[0], ID => $_->[1], ONLINE => 1 }, __get_nick_users($nick);
+}
+
+sub get_nick_users_all($) {
+ my ($nick) = @_;
+
+ return map +{ NICK => $_->[0], ID => $_->[1], ONLINE => $_->[2] }, __get_nick_users_all($nick);
}
1;
use Carp 'croak';
use Exporter 'import';
-BEGIN { our @EXPORT_OK = qw(spawn ima_worker $ima_worker multi get_socket call_in_parent call_all_child do_callback_in_child shutdown_worker shutdown_all_workers kill_all_workers) }
+BEGIN {
+ our @EXPORT_OK = qw(spawn write_pidfiles
+ ima_worker $ima_worker
+ multi get_socket
+ call_in_parent call_all_child do_callback_in_child
+ shutdown_worker shutdown_all_workers kill_all_workers)
+ }
use Event;
+use English qw( -no_match_vars );
use IO::Socket;
+use IO::File;
use Storable qw(fd_retrieve store_fd);
use SrSv::Debug;
+
+sub PREFIX() { return main::PREFIX }
+
BEGIN {
if(DEBUG) {
require Data::Dumper; import Data::Dumper ();
}
}
+sub write_pidfiles() {
+ my $fh = IO::File->new("@{[PREFIX]}/data/worker.pids", 'w', '0600');
+ for(my $i = scalar(@workers); $i; $i--) {
+ my $pid = $workers[$i-1]->{PID};
+ print $fh $pid,"\n";
+ }
+ print $fh $PID,"\n";
+}
+
sub ima_worker {
return $ima_worker;
}
sub do_callback_in_child {
my ($callback, $message) = @_;
+ # this whole thing is a workaround for perl 5.12's Storable.
+ # Can't pass a regexp through Storable.
+ if(ref($callback->{TRIGGER_COND}->{DST}) || ref($callback->{TRIGGER_COND}->{SRC})) {
+ foreach my $k (qw(DST SRC)) {
+ next unless defined $callback->{TRIGGER_COND}->{$k};
+ my $v = $callback->{TRIGGER_COND}->{$k};
+ $v = "$v"; # convert regexp to string
+ $callback->{TRIGGER_COND}->{$k} = $v;
+ }
+ #use Data::Dumper;
+ #ircd::debug( split($/, Data::Dumper::Dumper($worker->{UNIT})) );
+ }
if(my $worker = pop @free_workers) {
print "Asking worker ".$worker->{NUMBER}." to call ".$callback->{CALL}."\n" if DEBUG;
#store_fd([$unit], $worker->{SOCKET});
sub STORE {
my ($self, $key, $value) = @_;
- print "Store \%" . $$self . "\n" if SrSv::Shared::DEBUG;
+# print "Store \%" . $$self . "\n" if SrSv::Shared::DEBUG;
return ${$$self}{$key} = $value;
}
sub FETCH {
my ($self, $key) = @_;
- print "Fetch \%" . $$self . "\n" if SrSv::Shared::DEBUG;
+# print "Fetch \%" . $$self . "\n" if SrSv::Shared::DEBUG;
return ${$$self}{$key};
}
sub DELETE {
my ($self, $key) = @_;
+ print "DELETE \%" . $$self . "{$key}\n" if SrSv::Shared::DEBUG;
return delete(${$$self}{$key});
}
sub CLEAR {
my ($self) = @_;
-
+ print "CLEAR \%" . $$self . "\n" if SrSv::Shared::DEBUG;
+=cut
+ foreach my $key (keys %{$$self}) {
+ delete ($$self->{$key});
+ }
return %{$$self} = ();
+=cut
+ $$self = {};
+ return %{$$self};
}
sub EXISTS {
BEGIN { our @EXPORT_OK = qw(read_hash readHash write_hash writeHash) }
sub writeHash {
- my $hash = $_[0];
- my $file = $_[1];
+ my $hash = $_[0];
+ my $file = $_[1];
my $fh;
- open $fh, '>', $file;
+ open $fh, '>', $file;
- my @keys = keys(%$hash); my @values = values(%$hash);
+ my @keys = keys(%$hash); my @values = values(%$hash);
- for(my $i=0; $i<@keys; $i++) {
- if(ref($values[$i]) eq 'ARRAY') {
- chomp $keys[$i];
- print $fh $keys[$i], " =[ ";
- foreach my $atom (@{$values[$i]}) {
- print $fh $atom, ", ";
- }
- print $fh "\n";
- } else {
- chomp $keys[$i]; chomp $values[$i];
- print $fh $keys[$i], " = ", $values[$i], "\n";
- }
- }
+ for(my $i=0; $i<@keys; $i++) {
+ if(ref($values[$i]) eq 'ARRAY') {
+ chomp $keys[$i];
+ print $fh $keys[$i], " =[ ";
+ foreach my $atom (@{$values[$i]}) {
+ print $fh $atom, ", ";
+ }
+ print $fh "\n";
+ } else {
+ chomp $keys[$i]; chomp $values[$i];
+ print $fh $keys[$i], " = ", $values[$i], "\n";
+ }
+ }
- close $fh;
+ close $fh;
}
sub readHash {
while(my $line = <$fh>) {
if($line =~ /^#|^\s*$/) { }
- elsif($line =~ /^\S+ ?=\[ /) {
- my ($key, $value) = split(/ =\[ /, $line);
+ elsif($line =~ /^(\S+) ?= ?\[ ?(.*) ?]$/) {
+ my ($key, $value) = ($1, $2);
chomp $key; chomp $value;
$key =~ s/(^\s+|\s+$)//g;
$value =~ s/(^\s+|\s+$)//g;
$hash{$key} = [ split(/, /, $value) ];
}
elsif($line =~ /^\S+ ?= ?/) {
- my ($key, $value) = split(/ = /, $line);
+ my ($key, $value) = split(/ ?= ?/, $line, 2);
chomp $key; chomp $value;
if($value eq 'undef') {
$value = undef;
else {
die "Malformed config file: $file\n";
}
- }
- close $fh;
+ }
+ close $fh;
- return (%hash);
+ return (%hash);
}
BEGIN { # The same functions, now with less camelCase
sub openURI($) {
my ($URI) = @_;
- my $fh;
+ my $data;
if($URI =~ s/^file:\/\///i) {
- open($fh, '<', $URI) or die;
+ use IO::File;
+ my $fh = IO::File->new($URI, 'r') or die;
+ return $fh;
} else {
# assume HTTP/FTP URI
- open($fh, '-|', ('wget -q -O - ' . $URI)) or die;
+=cut use IO::Pipe;
+ my $fh = IO::Pipe->new();
+ $fh->reader(qq(wget -q -O - $URI)) or die;
+=cut
+ use WWW::Mechanize;
+ my $mech = WWW::Mechanize->new();
+ $mech->get($URI) or die $!;
+ my $content = $mech->content;
+ return $content;
}
- return $fh;
+}
+
+our %TOR_cmdhash;
+BEGIN {
+%TOR_cmdhash = (
+ 'r' => \&TOR_r,
+ 's' => \&TOR_s,
+ 'router' => \&TOR_router,
+ 'reject' => \&TOR_reject,
+ 'accept' => \&TOR_accept,
+);
}
sub parseTorRouterList($) {
my ($fh) = @_;
- my (%currentRouter, @routerList);
- while (my $l = <$fh>) {
+ our (%currentRouter, @routerList);
+ foreach my $l (ref($fh) ? <$fh> : split($/, $fh)) {
+ my ($tok, undef) = split(' ', $l, 2);
#print "$l";
chomp $l;
- if($l =~ /^r (\S)+ (?:[a-zA-Z0-9+\/]+) (?:[a-zA-Z0-9+\/]+) (?:\d{4}-\d{2}-\d{2} \d\d:\d\d:\d\d) (\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3}) (\d+) (\d+)/) {
+ if(my $code = $TOR_cmdhash{$tok}) {
+ &$code($l);
+ }
+ }
+ sub TOR_r {
+ my ($l) = @_;
#r atari i2i65Qm8DXfRpHVk6N0tcT0fxvs djULF2FbASFyIzuSpH1Zit9cYFc 2007-10-07 00:19:17 85.31.187.200 9001 9030
- #print "( NAME => $1, IP => \"$2.$3.$4.$5\", IN_PORT => $6, DIR_PORT => $7 )\n";
- %currentRouter = ( NAME => $1, IP => "$2.$3.$4.$5", IN_PORT => $6, DIR_PORT => $7 );
- }
- elsif($l =~ /^s (.*)/) {
+ my (undef, $name, undef, undef, undef, $ip, $in_port, $dir_port) = split(' ', $l);
+ %currentRouter = ( NAME => $name, IP => $ip, IN_PORT => $in_port, DIR_PORT => $dir_port );
+ return;
+ }
+ sub TOR_s {
+ my ($l) = @_;
+ if($l =~ /^s (.*)/) {
#s Exit Fast Guard Stable Running V2Dir Valid
my $tokens = $1;
# uncomment the conditional if you trust the router status flags
-# if($tokens =~ /Exit/) {
+ #if($tokens =~ /Exit/) {
push @routerList, $currentRouter{IP};
-# }
- }
- elsif($l =~ /router (\S+) (\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3}) (\d+) (\d+) (\d+)/) {
- push @routerList, processTorRouter(%currentRouter) if scalar(%currentRouter);
- %currentRouter = ( NAME => $1, IP => "$2.$3.$4.$5", IN_PORT => $6, DIR_PORT => $8 );
- } elsif($l =~ /reject (\S+):(\S+)/) {
- #print STDERR "$currentRouter{IP} reject $1:$2\n";
- push @{$currentRouter{REJECT}}, "$1:$2";
- } elsif($l =~ /accept (\S+):(\S+)/) {
- #print STDERR "$currentRouter{IP} accept $1:$2\n";
- push @{$currentRouter{ACCEPT}}, "$1:$2";
+ #}
}
}
- close $fh;
+ sub TOR_router {
+ my ($l) = @_;
+ my (undef, $name, $ip, $in_port, undef, $dir_port) = split(' ', $l);
+ push @routerList, processTorRouter(%currentRouter) if scalar(%currentRouter);
+ %currentRouter = ( NAME => $name, IP => $ip, IN_PORT => $in_port, DIR_PORT => $dir_port );
+ return;
+ }
+ sub TOR_reject {
+ my ($l) = @_;
+ my ($tok, $tuple) = split(' ', $l);
+ my ($ip, $ports) = split(':', $tuple);
+ push @{$currentRouter{REJECT}}, "$ip:$ports";
+ }
+ sub TOR_accept {
+ my ($l) = @_;
+ my ($tok, $tuple) = split(' ', $l);
+ my ($ip, $ports) = split(':', $tuple);
+ push @{$currentRouter{ACCEPT}}, "$ip:$ports";
+ }
+ #close $fh;
return @routerList;
}
use Encode 'encode';
use constant {
- MAX_WIDTH => 60,
+ MAX_WIDTH => 96,
COLORS => 1,
BULLET => encode('utf8', "\x{2022} "),
};
$t =~ s/^(.{60}.*?)\s*$/$1 / if length $t > 60;
$t = "\0031,15" . $t if $bg;
- return $t;
+ return split($/, $t);
}
} else {
*line_post = sub ($$) {
$t =~ s/ +$//;
$t = ' ' unless $t;
- return $t;
+ return split($/, $t);
}
} }
sub columnar(@) {
- my $opts = shift if ref($_[0]) eq 'HASH';
+ my $opts;
+ $opts = shift if ref($_[0]) eq 'HASH';
my (@mlen, @out);
$opts->{DOUBLE} = 0 if $opts->{NOHIGHLIGHT};
my $double = $opts->{DOUBLE};
my $border = $opts->{BORDER};
+ my $justified = $opts->{JUSTIFIED};
foreach my $x (@_) {
next unless ref($x) eq 'ARRAY';
}
my ($bg, $collapsed);
+ my $headerBorder = 0;
foreach my $x (@_) {
if(ref $x eq 'HASH') {
if(my $t = $x->{COLLAPSE}) {
next unless @$t;
- push @out, $borderLine if $border;
+ if($border) {
+ push @out, $borderLine;
+ }
push @out, ' ' unless $collapsed;
@$t = map BULLET . $_, @$t if($x->{BULLET});
push @out, @$t;
#my $border = '+'.'-'x($width+1).'+';
for(my $i; $i<@mlen; $i++) {
my $nc = strip_codes($x->[$i]);
- $str .= $x->[$i] .' ' x (($mlen[$i] - length($nc) + ($mlen[$i] ? 2 : 0))). ($border ? '| ' : ' ');
+ if($justified && $i == 0) {
+ $str .= ' ' x (($mlen[$i] - length($nc) + ($mlen[$i] ? 2 : 0))).
+ $x->[$i] . ($border ? '| ' : ' ');
+ } else {
+ $str .= $x->[$i] .' ' x (($mlen[$i] - length($nc) + ($mlen[$i] ? 2 : 0))).
+ ($border ? '| ' : ' ');
+ }
}
- push @out, $borderLine if $border;
+ if($border) {
+ if($headerBorder >= 2) {
+ } else {
+ push @out, $borderLine;
+ $headerBorder++
+ }
+ }
push @out, line_post $bg, $str;
if($double and $x->[-1]) {
continue {
$bg = !$bg unless $opts->{NOHIGHLIGHT};
}
- push @out, $borderLine if $border && !$collapsed;
+ push @out, $borderLine if $border && !$collapsed && scalar(@_)!=1;
push @out, ' (empty list)' unless @out;
push @out, ' --';
no integer; # We might want to pass in a float value for $difference
my ($difference) = @_;
my ($weeks, $days, $hours, $minutes, $seconds);
- $seconds = $difference % 60;
+ $seconds = $difference % 60 + ($difference - int($difference));
$difference = ($difference - $seconds) / 60;
$minutes = $difference % 60;
$difference = ($difference - $minutes) / 60;
($seconds!=1 ? 's' : '');
=cut
}
+ if(!($weeks || $days || $hours || $minutes || $seconds) ) {
+ return '0 seconds';
+ }
return $text;
}
=cut
use strict;
+use SrSv::64bit;
+BEGIN {
+ if(!HAS_64BIT_INT) {
+ eval {
+ require Math::BigInt;
+ import Math::BigInt try => 'GMP';
+ };
+ if($@) {
+ print STDERR "Running old version of perl/Math::BigInt.\n", $@, "Trying again.\n";
+ require Math::BigInt;
+ import Math::BigInt;
+ }
+ }
+}
use Exporter 'import';
BEGIN { our @EXPORT_OK = qw(b64toi itob64); }
*b64toi = \&base64_to_int;
sub base64_to_int($) {
my ($base64) = @_;
-
my $val = 0;
+ #wKgIAw==
+ if(length($base64) > 8) {
+ warn "greater-than-32bit base64($base64) in base64_to_int";
+ $val = (HAS_64BIT_INT ? 0 : Math::BigInt->bzero());
+ } else {
+ $val = 0;
+ }
+
foreach my $ch (split(//, $base64)) {
$val <<= 6;
$val += $base64_to_int6_map[ord($ch)];
BEGIN { *SJB64 = \&ircd::SJB64; *CLK = \&ircd::CLK; *NICKIP = \&ircd::NICKIP; }
use SrSv::Conf 'main';
+use SrSv::Conf2Consts 'main';
+
+use Socket;
+BEGIN {
+ if(main_conf_ipv6) {
+ require Socket6; import Socket6;
+ }
+}
use SrSv::Debug;
use SrSv::IRCd::State qw($ircline $remoteserv create_server get_server_children set_server_state get_server_state %IRCd_capabilities);
# tho MIME's is probably faster
use MIME::Base64;
-# FIXME
-use constant {
- # Wait For
- WF_NONE => 0,
- WF_NICK => 1,
- WF_CHAN => 2,
- WF_ALL => 3,
-};
+use SrSv::Constants;
use SrSv::Shared qw(@servernum);
}
$args[1] =~ s/\@${main_conf{local}}.*//io;
- if(queue_size > 50 and $event eq 'PRIVMSG' and $args[1] !~ /^#/ and $args[2] =~ /^\w/) {
- ircd::notice($args[1], $args[0], "It looks like the system is busy. You don't need to do your command again, just hold on a minute...");
+ if(queue_size(WF_MAX) > main_conf_queue_highwater) {
+ if($event eq 'PRIVMSG' and $args[1] !~ m'^#' and $args[2] =~ /^\w/) {
+ ircd::notice($args[1], $args[0],
+ "It looks like the system is busy. ".
+ "You don't need to do your command again, just hold on a minute...");
+ }
}
- return ($event, 0, 1, WF_ALL, @args);
+ return ($event, 0, 1, WF_MSG, @args);
}
sub AWAY($) {
}
elsif(CLK && NICKIP && $_[0] =~ /^(?:NICK|\&) (\S+) (\d+) (\S+) (\S+) (\S+) (\S+) (\d+) (\S+) (\S+) (\S+) (\S+) :(.*)$/) {
#NICK Guest57385 1 !14b7t0 northman tabriel.tabris.net 38 0 +iowghaAxNWzt netadmin.SCnet.ops SCnet-3B0714C4.tabris.net CgECgw== :Sponsored By Skuld
+#NICK outis 1 !14corv northman localhost 38 0 +iowghaAxNWzt tabris.netadmin.SCnet.ops SCnet-D8C01838 AAAAAAAAAAAAAAAAAAAAAQ== :Sponsored By Skuld
my ($nick, $hops, $ts, $ident, $host, $server, $stamp, $modes, $vhost, $cloakhost, $IP, $gecos) =
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12);
if ($ts =~ s/^!//) {
$server = $servernum[b64toi($server)];
}
- return ('NICKCONN', undef, undef, WF_NICK, $nick, $hops, $ts, $ident, $host, $server, $stamp, $modes, $vhost, $gecos,
- join('.', unpack('C4', MIME::Base64::decode($IP))), $cloakhost
+ if(main_conf_ipv6 && (length($IP) > 8)) {
+ $IP = Socket6::inet_ntop(AF_INET6, MIME::Base64::decode($IP));
+ } else {
+ $IP = join('.', unpack('C4', MIME::Base64::decode($IP)));
+ }
+ return ('NICKCONN', undef, undef, WF_NICK, $nick, $hops, $ts, $ident, $host, $server, $stamp, $modes, $vhost,
+ $gecos, $IP, $cloakhost
);
}
elsif(!CLK && NICKIP && $_[0] =~ /^(?:NICK|\&) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) :(.*)$/) {
$server = $servernum[b64toi($server)];
}
- return ('NICKCONN', undef, undef, WF_NICK, $nick, $hops, $ts, $ident, $host, $server, $stamp, $modes, $vhost, $gecos,
- join('.', unpack('C4', MIME::Base64::decode($IP)))
+ if(main_conf_ipv6 && length($IP) > 8) {
+ $IP = Socket6::inet_ntop(AF_INET6, MIME::Base64::decode($IP));
+ } else {
+ $IP = join('.', unpack('C4', MIME::Base64::decode($IP)));
+ }
+ return ('NICKCONN', undef, undef, WF_NICK, $nick, $hops, $ts, $ident, $host, $server, $stamp, $modes, $vhost,
+ $gecos, $IP
);
}
elsif(!CLK && !NICKIP && $_[0] =~ /^(?:NICK|\&) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) :(.*)$/) {
$server = $servernum[b64toi($server)];
}
- return ('NICKCONN', undef, undef, WF_NICK, $nick, $hops, $ts, $ident, $host, $server, $stamp, $modes, $vhost, $gecos);
+ return ('NICKCONN', undef, undef, WF_NICK, $nick, $hops, $ts, $ident, $host, $server, $stamp, $modes,
+ $vhost, $gecos);
}
}
sub TOPIC($) {
if($_[0] =~ /^(@|:)(\S+) (?:TOPIC|\)) (\S+) (\S+) (\S+) :(.*)$/) {
#:tabris TOPIC #the_lounge tabris 1089336598 :Small Channel in search of Strong Founder for long term relationship, growth, and great conversation.
- my $name;
- if ($1 eq '@') {
- $name = $servernum[b64toi($2)];
- }
- else {
- $name = $2;
- }
- return ('TOPIC', 0, 1, WF_ALL, $name, $3, $4, $5, $6);
+ my $name;
+ my ($name, $cn, $setter, $ts, $topic) = ($2, $3, $4, $5, $6);
+ if ($1 eq '@') {
+ $name = $servernum[b64toi($2)];
+ }
+ else {
+ $name = $2;
+ }
+ if ($ts =~ s/^!//) {
+ $ts = b64toi($ts);
+ }
+ return ('TOPIC', 0, 1, WF_ALL, $name, $cn, $setter, $ts, $topic);
}
elsif($_[0] =~ /^(?:TOPIC|\)) (\S+) (\S+) (\S+) :(.*)$/) {
+ my ($cn, $setter, $ts, $topic) = ($1, $2, $3, $4);
+ if ($ts =~ s/^!//) {
+ $ts = b64toi($ts);
+ }
# src, channel, setter, timestamp, topic
- return ('TOPIC', 0, 1, WF_ALL, undef, $1, $2, $3, $4);
+ return ('TOPIC', 0, 1, WF_ALL, undef, $cn, $setter, $ts, $topic);
}
}
use MIME::Base64;
use SrSv::Conf 'main';
+use SrSv::Conf::main;
+use SrSv::Conf2Consts qw( main );
use SrSv::Debug;
use SrSv::Log;
use SrSv::IRCd::State qw($ircline $remoteserv $ircd_ready synced initial_synced set_server_state set_server_juped get_server_state get_online_servers);
use SrSv::Unreal::Modes qw(@opmodes %opmodes $scm $ocm $acm);
-use SrSv::Unreal::Tokens;
+use SrSv::Unreal::Tokens qw( :tokens );
use SrSv::IRCd::Parse qw(parse_tkl);
use SrSv::Unreal::Base64 qw(itob64 b64toi);
addhandler('SERVER', undef(), undef(), 'ircd::handle_server', 1);
sub serv_connect() {
- my $remote = $main_conf{remote};
- my $port = $main_conf{port};
+ my $remote = main_conf_remote;
+ my $port = main_conf_port;
ircd_connect($remote, $port);
- ircsendimm('PROTOCTL '.($tkn ? 'TOKEN ' : '').'NICKv2 UMODE2 TKLEXT'.
+ ircsendimm('PROTOCTL '.(main_conf_tokens ? 'TOKEN ' : '').'NICKv2 UMODE2 TKLEXT'.
(CLK ? ' CLK' : ' VHP'). # CLK obsoletes VHP. Plus if you leave VHP on, CLK doesn't work.
(NOQUIT ? ' NOQUIT' : '').(SJ3 ? ' SJOIN SJOIN2 SJ3' : '').
(NICKIP ? ' NICKIP' : '').
(SJB64 ? ' SJB64 NS VL' : ''),
- 'PASS :'.$main_conf{pass},
- 'SERVER '.$main_conf{local}.' 1 '.$main_conf{numeric}.(SJB64 ? ( ':U*-*-'.$main_conf{numeric}.' ') : ' :').$main_conf{info});
+ 'PASS :'.main_conf_pass,
+ 'SERVER '.main_conf_local.' 1 '.main_conf_numeric.(SJB64 ? ( ':U*-*-'.main_conf_numeric.' ') : ' :').main_conf_info);
%preconnect_defer_mode = %defer_mode;
%defer_mode = ();
# $dst is always $main_conf{local} anyway...
# this is only valid b/c we never have messages routed THROUGH us
# we are always an end point.
- ircsendimm(":$dst $tkn{PONG}[$tkn] $src :$cookie");
+ ircsendimm(":$dst @{[TOK_PONG]} $src :$cookie");
}
else {
- ircsendimm("$tkn{PONG}[$tkn] :$src");
+ ircsendimm("@{[TOK_PONG]} :$src");
}
}
#print "Synced: ", synced(), "\n\n";
#exit;
- ircsendimm(':'.$main_conf{local}.' '.$tkn{EOS}[$tkn], 'VERSION');
+ ircsendimm(':'.main_conf_local.' '.TOK_EOS, 'VERSION');
agent_sync();
flushmodes(\%preconnect_defer_mode);
}
sub netinfo($$$$$$$$) {
- ircsendimm($tkn{NETINFO}[$tkn].' 0 '.time." $_[2] $_[3] 0 0 0 :$_[7]");
+ ircsendimm(TOK_NETINFO.' 0 '.time." $_[2] $_[3] 0 0 0 :$_[7]");
$main_conf{network} = $_[7];
}
sub tssync {
- ircsendimm((SJB64 ? '@'.itob64($main_conf{numeric}) : ':'.$main_conf{local})." $tkn{TSCTL}[$tkn] SVSTIME ".time);
+ ircsendimm((SJB64 ? '@'.itob64(main_conf_numeric) : ':'.main_conf_local)." @{[TOK_TSCTL]} SVSTIME ".time);
}
sub parse_sjoin($$$$) {
sub kick($$$$) {
my ($src, $chan, $target, $reason) = @_;
- $src = $main_conf{local} unless initial_synced();
- ircsend(":$src $tkn{KICK}[$tkn] $chan $target :$reason");
-# thread::ircrecv(":$src $tkn{KICK}[$tkn] $chan $target :$reason");
+ $src = main_conf_local unless initial_synced();
+ ircsend(":$src @{[TOK_KICK]} $chan $target :$reason");
+# thread::ircrecv(":$src @{[TOK_KICK]} $chan $target :$reason");
callfuncs('KICK', 0, 2, [$src, $chan, $target, $reason]);
}
sub invite($$$) {
my ($src, $chan, $target) = @_;
#:SecurityBot INVITE tabris #channel
- ircsend(":$src $tkn{INVITE}[$tkn] $target $chan");
+ ircsend(":$src @{[TOK_INVITE]} $target $chan");
}
sub ping {
# if(@_ == 1) {
- ircsend(':'.$main_conf{local}.' '.$tkn{PING}[$tkn].' :'.$main_conf{local});
+ ircsend(':'.main_conf_local.' '.TOK_PING.' :'.main_conf_local);
# } else {
-# ircsend(':'.$_[2].' '.$tkn{PONG}[$tkn].' '.$_[0].' :'.$_[1]);
+# ircsend(':'.$_[2].' '.TOK_PONG.' '.$_[0].' :'.$_[1]);
# }
}
-sub privmsg($$@) {
+sub __privmsg($$@) {
my ($src, $dst, @msgs) = @_;
my @bufs;
# Length restrictions are for CLIENT Protocol
# hence the (MASKLEN - (NICKLEN + 1))
# Technically optimizable if we use $agent{lc $src}'s ident and host
- my $buflen = length($src) + length($dst) + 12 + (MASKLEN - (NICKLEN + 1));
+ my $buflen = length($src) + length($dst) + 5 + length(TOK_PRIVMSG) + (MASKLEN - (NICKLEN + 1));
push @bufs, wordwrap($buf, (MAXBUFLEN - $buflen));
}
# submit a list of messages as a single packet to the server
- ircsend(":$src $tkn{PRIVMSG}[$tkn] $dst :".join("\r\n".":$src $tkn{PRIVMSG}[$tkn] $dst :", @bufs));
- callfuncs('LOOP_PRIVMSG', 0, 1, [$src, $dst, \@bufs]);
+ ircsend(":$src @{[TOK_PRIVMSG]} $dst :".join("\r\n".":$src @{[TOK_PRIVMSG]} $dst :", @bufs));
+ return \@bufs;
+}
+sub privmsg($$@) {
+ my ($src, $dst, @msgs) = @_;
+ my $bufs = __privmsg($src, $dst, @msgs);
+ callfuncs('LOOP_PRIVMSG', 0, 1, [$src, $dst, $bufs]);
+}
+sub privmsg_noloop($$@) {
+ my ($src, $dst, @msgs) = @_;
+ __privmsg($src, $dst, @msgs);
+ return;
}
sub debug(@) {
my (@msgs) = @_;
- debug_privmsg($main_conf{local}, $main_conf{diag}, @msgs);
- write_log('diag', '<'.$main_conf{local}.'>', @msgs);
+ privmsg(main_conf_local, main_conf_diag, @msgs);
+ write_log('diag', '<'.main_conf_local.'>', @msgs);
}
sub debug_nolog(@) {
my (@msgs) = @_;
- debug_privmsg($main_conf{local}, $main_conf{diag}, @msgs);
+ privmsg(main_conf_local, main_conf_diag, @msgs);
}
-sub debug_privmsg($$@) {
- my ($src, $dst, @msgs) = @_;
-
- my @bufs;
- foreach my $buf (@msgs) {
- # 3 spaces, two colons, PRIVMSG=7
- # Length restrictions are for CLIENT Protocol
- # hence the (MASKLEN - (NICKLEN + 1))
- my $buflen = length($src) + length($dst) + 12 + (MASKLEN - (NICKLEN + 1));
- push @bufs, wordwrap($buf, (MAXBUFLEN - $buflen));
- }
-
- # submit a list of messages as a single packet to the server
- ircsendimm(":$src $tkn{PRIVMSG}[$tkn] $dst :".join("\r\n".":$src $tkn{PRIVMSG}[$tkn] $dst :", @bufs));
- callfuncs('LOOP_PRIVMSG', 0, 1, [$src, $dst, \@bufs]);
-}
sub notice($$@) {
my ($src, $dst, @msgs) = @_;
# 3 spaces, two colons, NOTICE=6
# Length restrictions are for CLIENT Protocol
# hence the (MASKLEN - (NICKLEN + 1))
- my $buflen = length($src) + length($dst) + 12 + (MASKLEN - (NICKLEN + 1));
+ my $buflen = length($src) + length($dst) + 5 + length(TOK_NOTICE) + (MASKLEN - (NICKLEN + 1));
push @bufs, wordwrap($buf, (MAXBUFLEN - $buflen));
}
# submit a list of notices as a single packet to the server
- ircsend(":$src $tkn{NOTICE}[$tkn] $dst :".join("\r\n".":$src $tkn{NOTICE}[$tkn] $dst :", @bufs));
+ ircsend(":$src @{[TOK_NOTICE]} $dst :".join("\r\n".":$src @{[TOK_NOTICE]} $dst :", @bufs));
callfuncs('LOOP_NOTICE', 0, 1, [$src, $dst, \@bufs]);
}
sub setumode($$$) {
my ($src, $dst, $modes) = @_;
- ircsend(":$src $tkn{SVS2MODE}[$tkn] $dst $modes");
+ ircsend(":$src @{[TOK_SVS2MODE]} $dst $modes");
callfuncs('UMODE', 0, undef, [$dst, $modes]);
}
sub setsvsstamp($$$) {
my ($src, $dst, $stamp) = @_;
- ircsend(":$src $tkn{SVS2MODE}[$tkn] $dst +d $stamp");
+ ircsend(":$src @{[TOK_SVS2MODE]} $dst +d $stamp");
# This function basically set the svsstamp to
# be the same as the userid. Not all ircd will
# support this function.
sub setagent_umode($$) {
my ($src, $modes) = @_;
- ircsend(":$src $tkn{UMODE2}[$tkn] $modes");
+ ircsend(":$src @{[TOK_UMODE2]} $modes");
}
sub setmode2($$@) {
sub setmode($$$;$) {
my ($src, $dst, $modes, $parms) = @_;
- $src = $main_conf{local} unless initial_synced();
+ $src = main_conf_local unless initial_synced();
callfuncs('MODE', undef, 1, [$src, $dst, $modes, $parms]);
print "$ircline -- setmode_real($src, $dst, $modes, $parms)\n" if DEBUG;
# for server sources, there must be a timestamp. but you can put 0 for unspecified.
$parms =~ s/\s+$//; #trim any trailing whitespace, as it might break the simple parser in the ircd.
- ircsend(":$src ".$tkn{MODE}[$tkn]." $dst $modes".($parms?" $parms":'').($src =~ /\./ ? ' 0' : ''));
+ ircsend(":$src @{[TOK_MODE]} $dst $modes".($parms?" $parms":'').($src =~ /\./ ? ' 0' : ''));
}
sub settopic($$$$$) {
my ($src, $chan, $setter, $time, $topic) = @_;
- $src = $main_conf{local} unless initial_synced();
+ $src = main_conf_local unless initial_synced();
- ircsend(":$src ".$tkn{TOPIC}[$tkn]." $chan $setter $time :$topic");
+ ircsend(":$src @{[TOK_TOPIC]} $chan $setter $time :$topic");
callfuncs('TOPIC', undef, undef, [$src, $chan, $setter, $time, $topic]);
}
sub wallops ($$) {
my ($src, $message) = @_;
- ircsend(":$src $tkn{WALLOPS}[$tkn] :$message");
+ ircsend(":$src @{[TOK_WALLOPS]} :$message");
}
sub globops ($$) {
my ($src, $message) = @_;
- ircsend(":$src $tkn{GLOBOPS}[$tkn] :$message");
+ ircsend(":$src @{[TOK_GLOBOPS]} :$message");
}
sub kline ($$$$$) {
my ($setter, $ident, $host, $expiry, $reason) = @_;
- $setter=$main_conf{local} unless defined($setter);
+ $setter=main_conf_local unless defined($setter);
$ident = '*' unless defined($ident);
sub zline ($$$$) {
my ($setter, $host, $expiry, $reason) = @_;
- $setter=$main_conf{local} unless defined($setter);
+ $setter=main_conf_local unless defined($setter);
#foreach my $ex (@except) { return 1 if $mask =~ /\Q$ex\E/i; }
sub irckill($$$) {
my ($src, $targetlist, $reason) = @_;
- $src = $main_conf{local} unless initial_synced();
+ $src = main_conf_local unless initial_synced();
foreach my $target (split(',', $targetlist)) {
next unless update_userkill($target);
- ircsendimm(":$src ".$tkn{KILL}[$tkn]." $target :$src ($reason)");
+ ircsendimm(":$src @{[TOK_KILL]} $target :$src ($reason)");
callfuncs('KILL', 0, 1, [$src, $target, $src, $reason]);
}
sub svssno($$$) {
my ($src, $target, $snomasks) = @_;
- $src=$main_conf{local} unless defined($src);
+ $src=main_conf_local unless defined($src);
# TODO:
# None, this doesn't affect us.
sub svsnick($$$) {
my ($src, $oldnick, $newnick) = @_;
- $src=$main_conf{local} unless defined($src);
+ $src=main_conf_local unless defined($src);
# note: we will get a NICK cmd back after a
# successful nick change.
# warning, if misused, this can KILL the user
# with a collision
-# ircsend(":$src ".$tkn{SVSNICK}[$tkn]." $oldnick $newnick ".time);
- ircsend($tkn{SVSNICK}[$tkn]." $oldnick $newnick :".time);
+# ircsend(":$src @{[TOK_SVSNICK]} $oldnick $newnick ".time);
+ ircsend("@{[TOK_SVSNICK]} $oldnick $newnick :".time);
}
sub svsnoop($$$) {
my ($targetserver, $bool, $src) = @_;
- $src = $main_conf{local} unless defined($src);
+ $src = main_conf_local unless defined($src);
if ($bool > 0) { $bool = '+'; } else { $bool = '-'; }
#this is SVS NO-OP not SVS SNOOP
- ircsend(":$main_conf{local} $tkn{SVSNOOP}[$tkn] $targetserver $bool");
+ ircsend(":@{[main_conf_local]} @{[TOK_SVSNOOP]} $targetserver $bool");
}
sub svswatch ($$@) {
# Example: SVSWATCH Blah :+Blih!*@* -Bluh!*@* +Bleh!*@*.com
# *** We do not track this info nor care.
my ($src, $target, @watchlist) = @_;
- my $base_str = ":$src ".$tkn{SVSWATCH}[$tkn]." $target :";
+ my $base_str = ":$src @{[TOK_SVSWATCH]} $target :";
my $send_str = $base_str;
while (@watchlist) {
my $watch = shift @watchlist;
# Example: SVSSILENCE Blah :+Blih!*@* -Bluh!*@* +Bleh!*@*.com
# *** We do not track this info nor care.
my ($src, $target, @silencelist) = @_;
- my $base_str = ":$src ".$tkn{SVSSILENCE}[$tkn]." $target :";
+ my $base_str = ":$src @{[TOK_SVSSILENCE]} $target :";
my $send_str = $base_str;
while (@silencelist) {
my $silence = shift @silencelist;
# *** this cmd does not change any umodes!
my ($src, $target, $oflags) = @_;
- $src = $main_conf{local} unless defined($src);
- ircsend(":$src $tkn{SVSO}[$tkn] $target $oflags");
+ $src = main_conf_local unless defined($src);
+ ircsend(":$src @{[TOK_SVSO]} $target $oflags");
}
sub swhois($$$) {
# *** We do not track this info nor care.
my ($src, $target, $swhois) = @_;
- $src = $main_conf{local} unless defined($src);
- ircsend(":$src $tkn{SWHOIS}[$tkn] $target :$swhois");
+ $src = main_conf_local unless defined($src);
+ ircsend(":$src @{[TOK_SWHOIS]} $target :$swhois");
}
sub svsjoin($$@) {
# a note. a JOIN is returned back to us on success
# so no need to process this command.
# similar for svspart.
- ircsend(($src?":$src":'')." $tkn{SVSJOIN}[$tkn] $target ".join(',', @chans));
+ ircsend(($src?":$src":'')." @{[TOK_SVSJOIN]} $target ".join(',', @chans));
}
sub svspart($$$@) {
my ($src, $target, $reason, @chans) = @_;
- ircsend(($src ? ":$src" : '')." $tkn{SVSPART}[$tkn] $target ".join(',', @chans).
+ ircsend(($src ? ":$src" : '')." @{[TOK_SVSPART]} $target ".join(',', @chans).
($reason ? " :$reason" : ''));
}
# tho whether we want to put it in agent_connect
# or leave it to the module to call it...
my ($nickmask, $reason) = @_;
- #ircsend("$tkn{S1QLINE}[$tkn] $nickmask".($reason?" :$reason":''));
+ #ircsend("@{[TOK_SQLINE]} $nickmask".($reason?" :$reason":''));
qline($nickmask, 0, $reason);
}
my ($nickmask, $expiry, $reason) = @_;
# TKL version - Allows timed qlines.
# TKL + Q * test services.SC.net 0 1092179497 :test
- my $line = 'TKL + Q H '.$nickmask.' '.$main_conf{local}.' '.($expiry ? $expiry+time() : 0).' '.time().' :'.$reason;
+ my $line = 'TKL + Q H '.$nickmask.' '.main_conf_local.' '.($expiry ? $expiry+time() : 0).' '.time().' :'.$reason;
ircsend($line);
# at startup we send these too early,
my ($nickmask) = @_;
# TKL version
# TKL - Q * test services.SC.net
- my $line = 'TKL - Q H '.$nickmask.' '.$main_conf{local};
+ my $line = 'TKL - Q H '.$nickmask.' '.main_conf_local;
ircsend($line);
callfuncs('TKL', undef, undef, [parse_tkl($line)]);
}
my ($nickmask, $expiry, $reason) = @_;
# TKL version - Allows timed qlines.
# TKL + Q * test services.SC.net 0 1092179497 :test
- my $line = 'TKL + Q * '.$nickmask.' '.$main_conf{local}.' '.($expiry ? $expiry+time() : 0).' '.time().' :'.$reason;
+ my $line = 'TKL + Q * '.$nickmask.' '.main_conf_local.' '.($expiry ? $expiry+time() : 0).' '.time().' :'.$reason;
ircsend($line);
# at startup we send these too early,
my ($nickmask) = @_;
# TKL version
# TKL - Q * test services.SC.net
- my $line = 'TKL - Q * '.$nickmask.' '.$main_conf{local};
+ my $line = 'TKL - Q * '.$nickmask.' '.main_conf_local;
ircsend($line);
callfuncs('TKL', undef, undef, [parse_tkl($line)]);
}
# not sure if it'll accept a servername or not.
# consider defaulting to ServServ
die('svskill called w/o $src') unless $src;
- ircsend(':'.$src.' '.$tkn{SVSKILL}[$tkn].' '.$target.' :'.$reason);
+ ircsend(':'.$src.' '.TOK_SVSKILL.' '.$target.' :'.$reason);
callfuncs('QUIT', 0, undef, [$target, $reason]);
}
sub version($) {
my ($src) = @_;
- ircsend(":$main_conf{local} 351 $src $main::progname ver $main::version $main_conf{local} ".
+ ircsend(":@{[main_conf_local]} 351 $src $main::progname ver $main::version @{[main_conf_local]} ".
$main::extraversion);
}
sub userhost($) {
my ($target) = @_;
- ircsend($tkn{USERHOST}[$tkn]." $target");
+ ircsend("@{[TOK_USERHOST]} $target");
}
sub userip($) {
sub chghost($$$) {
my ($src, $target, $vhost) = @_;
- ircsend(($src?":$src ":'').$tkn{CHGHOST}[$tkn]." $target $vhost");
+ ircsend(($src?":$src ":'')."@{[TOK_CHGHOST]} $target $vhost");
callfuncs('CHGHOST', 0, 1, [$src, $target, $vhost]);
}
sub chgident($$$) {
my ($src, $target, $ident) = @_;
- ircsend(($src?":$src ":'').$tkn{CHGIDENT}[$tkn]." $target $ident");
+ ircsend(($src?":$src ":'')."@{[TOK_CHGIDENT]} $target $ident");
callfuncs('CHGIDENT', 0, 1, [$src, $target, $ident]);
}
# :nascent.surrealchat.net SERVER wyvern.surrealchat.net 2 :SurrealChat
die "You can't jupe $server"
- if ((lc($server) eq lc($remoteserv)) or (lc($server) eq lc($main_conf{local})));
- ircsend(':'.$main_conf{local}.' '.$tkn{SQUIT}[$tkn]." $server :");
- ircsend(':'.$main_conf{local}.' '.$tkn{SERVER}[$tkn]." $server 2 :$reason");
+ if ((lc($server) eq lc($remoteserv)) or (lc($server) eq lc(main_conf_local)));
+ ircsend(':'.main_conf_local.' '."@{[TOK_SQUIT]} $server :");
+ ircsend(':'.main_conf_local.' '."@{[TOK_SERVER]} $server 2 :$reason");
set_server_juped($server);
}
$type = undef() if(defined($type) && !($type =~ /^\-(motd|botmotd|opermotd|garbage)$/i));
foreach my $server (get_online_servers()) {
- ircsend(':'.$main::rsnick.' '.$tkn{REHASH}[$tkn].' '.$server.(defined($type) ? ' '.$type : '') );
+ ircsend(':'.$main::rsnick.' '.TOK_REHASH.' '.$server.(defined($type) ? ' '.$type : '') );
}
}
while(my $nick = shift @nicks) {
push @nicklist, $nick;
if(++$i >= 10) {
- ircsend(($src ? ":$src " : '' ).$tkn{SVSMODE}[$tkn]." $cn -".'b'x($i).' '.join(' ', @nicklist));
+ ircsend(($src ? ":$src " : '' )."@{[TOK_SVSMODE]} $cn -".'b'x($i).' '.join(' ', @nicklist));
$i = 0; @nicklist = ();
}
}
- ircsend(($src ? ":$src " : '' ).$tkn{SVSMODE}[$tkn]." $cn -".'b'x($i).' '.join(' ', @nicklist));
+ ircsend(($src ? ":$src " : '' )."@{[TOK_SVSMODE]} $cn -".'b'x($i).' '.join(' ', @nicklist));
# We don't loopback this, as we'll receive back the list
# of removed bans.
}
# Similar concepts may exist in other ircd implementations
my ($src, $cn) = @_;
- ircsend(($src ? ":$src " : '' ).$tkn{SVSMODE}[$tkn]." $cn -b");
+ ircsend(($src ? ":$src " : '' )."@{[TOK_SVSMODE]} $cn -b");
# We don't loopback this, as we'll receive back the list
# of removed bans.
}
sub nolag($$@) {
my ($src, $sign, @targets) = @_;
- $src = $main_conf{local} unless $src;
+ $src = main_conf_local unless $src;
foreach my $target (@targets) {
- ircsend(':'.$src .' '.$tkn{SVS2NOLAG}[$tkn].' '.$sign.' '.$target);
+ ircsend(':'.$src .' '.TOK_SVS2NOLAG.' '.$sign.' '.$target);
}
}
use strict;
+use SrSv::Conf::main;
+use SrSv::Conf2Consts qw( main );
+
use Exporter 'import';
-BEGIN { our @EXPORT = qw($tkn %tkn) }
-our $tkn = 0;
+#=cut
+our $tkn = main_conf_tokens;
+our %tkn;
+#=cut
+BEGIN {
# TODO: Turn these into constants.
-our %tkn = (
+my %unrealTokens = (
PRIVMSG => ['PRIVMSG', '!'],
WHOIS => ['WHOIS', '#'],
WHOWAS => ['WHOWAS', '$'],
SVS2NOLAG => ['SVS2NOLAG', 'SL'],
);
+ %tkn = %unrealTokens;
+ my %msgs; map { $msgs{"MSG_$_"} = $unrealTokens{$_}->[0] } keys(%unrealTokens);
+ my %toks;
+ if(main_conf_tokens) {
+ map { $toks{"TOK_$_"} = $unrealTokens{$_}->[1] } keys(%unrealTokens);
+ } else {
+ map { $toks{"TOK_$_"} = $unrealTokens{$_}->[0] } keys(%unrealTokens);
+ }
+ require constant;
+ import constant \%toks;
+ import constant \%msgs;
+
+ our @EXPORT_OK = (
+ keys(%toks),
+ keys(%msgs),
+ qw( %tkn $tkn )
+ );
+ our %EXPORT_TAGS = (
+ tokens => [keys(%toks)],
+ messages => [keys(%msgs)],
+ );
+}
+
1;
my ($parm) = @_;
my ($type, $payload) = split(':', $parm, 2);
$type =~ s/^\~//;
- if(lc $type eq 'q' or lc $type eq 'n') {
+ if($type eq 'q' or $type eq 'n') {
return 1 if($payload =~ /^(.+)!(.+)@(.+)$/);
- } elsif(lc $type eq 'c') {
+ } elsif($type eq 'c') {
return 1 if($payload =~ /^[~&@%+]?#.{0,29}$/);
- } elsif(lc $type eq 'r') {
+ } elsif($type eq 'r') {
return 1; # how can this be invalid anyway?
- } elsif(uc $type eq 'T') {
+ } elsif($type eq 'T') {
my ($action, $mask) = split(':', $payload);
return 1 if ($action =~ /^(block|censor)$/i);
}
BEGIN {
my %constants = (
UF_FINISHED => 1,
- UF_ONLINE => 2, # not used yet
+ UF_GUEST => 2,
);
our @EXPORT_OK = (qw(get_user_id get_user_nick get_user_agent is_online chk_online
get_user_flags set_user_flag chk_user_flag set_user_flag_all
get_host get_vhost get_cloakhost get_user_info
flood_inc flood_check get_flood_level
+ kill_user kline_user
__flood_expire
),
keys(%constants));
}
use SrSv::MySQL::Stub {
- __getIPV4 => ['SCALAR', "SELECT INET_NTOA(ip) FROM user WHERE id=?"],
+ __getIP => ['ROW', "SELECT INET_NTOA(ip), ipv6 FROM user WHERE id=?"],
};
+use SrSv::IRCd::Send; #package ircd
use SrSv::Process::Init;
use SrSv::MySQL '$dbh';
use SrSv::NickControl::Enforcer qw(%enforcers);
use SrSv::Agent qw(is_agent);
use SrSv::User::Notice;
+use SrSv::Conf::services;
+use SrSv::Conf::main;
+use SrSv::Conf2Consts qw( main services );
+
+use SrSv::IPv6;
+
use SrSv::Log;
our (
$get_user_flags, $set_user_flag, $unset_user_flag, $set_user_flag_all,
- $get_host, $get_vhost, $get_cloakhost, $get_ip, $get_user_info,
+ $get_host, $get_vhost, $get_cloakhost,
);
proc_init {
$get_host = $dbh->prepare("SELECT ident, host FROM user WHERE id=?");
$get_vhost = $dbh->prepare("SELECT ident, vhost FROM user WHERE id=?");
$get_cloakhost = $dbh->prepare("SELECT 1, cloakhost FROM user WHERE id=?");
- $get_user_info = $dbh->prepare("SELECT ident, host, vhost, gecos, server FROM user WHERE id=?");
};
require SrSv::MySQL::Stub;
import SrSv::MySQL::Stub {
__flood_check => ['SCALAR', "SELECT flood FROM user WHERE id=?"],
__flood_inc => ['NULL', "UPDATE user SET flood = flood + ? WHERE id=?"],
__flood_expire => ['NULL', "UPDATE user SET flood = flood >> 1"], # shift is faster than mul
+
+ __get_user_info => ['ROW', "SELECT ident, host, vhost, gecos, server, time, quittime
+ FROM user WHERE id=?"],
};
sub get_flood_level($) {
my $uid = get_user_id($user);
return undef() unless $uid;
- $get_user_info->execute($uid);
- my ($ident, $host, $vhost, $gecos, $server) = $get_user_info->fetchrow_array();
- $get_user_info->finish;
-
- return ($ident, $host, $vhost, $gecos, $server);
+ return __get_user_info($uid);
}
+=cut
sub get_user_ipv4($) {
my ($user) = @_;
return $ip;
}
}
+=cut
sub get_user_ip($) {
- return get_user_ipv4(@_);
+ my ($user) = @_;
+
+ my $id;
+ if (ref($user)) {
+ if(exists $user->{IP}) {
+ return $user->{IP};
+ }
+ $id = get_user_id($user);
+ } else {
+ $id = get_user_id({ NICK => $user});
+ }
+ return undef unless $id;
+
+ my ($ipv4,$ipv6) = __getIP($id);
+ if (defined $ipv6) {
+ return $user->{IP} = $ipv6 unless !ref($user);
+ return $ipv6;
+ } else {
+ return $user->{IP} = $ipv4 unless !ref($user);
+ return $ipv4;
+ }
+}
+
+sub kill_user($$) {
+ my ($user, $reason) = @_;
+
+ ircd::irckill(get_user_agent($user) || main_conf_local, get_user_nick($user), $reason);
+}
+
+sub kline_user($$$) {
+ my ($user, $time, $reason) = @_;
+ my $agent = get_user_agent($user);
+ my ($ident, $host) = get_host($user);
+
+ ircd::kline($agent, '*', $host, $time, $reason);
}
1;
--- /dev/null
+# This file is part of SurrealServices.
+#
+# SurrealServices is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# SurrealServices is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with SurrealServices; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package SrSv::User::Notice;
+
+use strict;
+
+use Exporter 'import';
+BEGIN { our @EXPORT = qw(add_user_tag get_user_tags check_user_tags) }
+
+use SrSv::User qw(get_user_nick get_user_id);
+
+use SrSv::MySQL::Stub (
+ __add_user_tag => ['INSERT', "INSERT IGNORE INTO usertags (userid, tag) VALUES (?,?)"],
+ __get_user_tags => ['COLUMN', 'SELECT tag FROM usertags WHERE userid=?'],
+ __check_user_tags => ['SCALAR', 'SELECT 1 FROM usertags WHERE userid=? AND tag=?'],
+);
+
+sub add_user_tag($$) {
+ my ($user, $tag) = @_;
+ return __add_user_tag(get_user_id($user), $tag);
+}
+sub get_user_tags($$) {
+ my ($user, $tag) = @_;
+ return __get_user_tags(get_user_id($user));
+}
+sub check_user_tags($$) {
+ my ($user, $tag) = @_;
+ return __check_user_tag(get_user_id($user), $tag);
+}
+
+1;
use Exporter 'import';
BEGIN {
- our @EXPORT = qw(min max makeSeqList);
+ our @EXPORT = qw(min max makeSeqList seqifyList);
our @EXPORT_OK = qw(
say say2 say3 sayFH sayERR
slurpFile dumpFile
interpretSuffixes humanizeBigNums
+ unique countUnique
);
our %EXPORT_TAGS = (
say => [qw( say say2 say3 sayFH sayERR )],
return ($_[0] > $_[1] ? $_[0] : $_[1]);
}
+# This one only exists b/c it should be faster/simpler
+# than the unique() below
+sub __uniq(@) {
+ return keys %{{ map { $_ => 1 } @_ }}
+}
+# the sort is modified to sort numerically rather than by string.
+sub __numSort(@) {
+ return sort( {$a <=> $b} @_ );
+}
+
sub makeSeqList(@) {
my @nums;
foreach my $arg (@_) {
}
}
# map is a uniqify in case of duplicates
- # the sort is modified to sort numerically rather than by string.
- return sort( {$a <=> $b} keys %{{ map { $_ => 1 } @nums }} );
+ return __numSort( __uniq(@nums) );
+}
+
+sub __seqify($$) {
+ my ($lowNum, $highNum) = @_;
+ if($lowNum == $highNum) {
+ return $lowNum;
+ } else {
+ return "${lowNum}..${highNum}";
+ }
+}
+sub seqifyList(@) {
+ my @nums = __numSort( __uniq(@_) );
+ my $lowNum = shift @nums;
+ my $highNum = $lowNum;
+ my @seqs;
+ foreach my $num (@nums) {
+ if($num == ($highNum + 1)) {
+ # one could also $highNum++
+ # which would on a register-based CPU/VM be potentially faster
+ # and only use one register, assuming it implemented it via inc(reg)
+ # otoh, $num is already loaded into a reg, right?
+ $highNum = $num;
+ } else {
+ push @seqs, __seqify($lowNum, $highNum);
+ $lowNum = $highNum = $num;
+ }
+ }
+ push @seqs, __seqify($lowNum, $highNum);
+ return @seqs;
}
sub __say($@) {
print $fh _say(@list);
}
sub sayERR(@) {
- sayFH STDERR, @_;
+ sayFH(*STDERR, @_);
}
sub say2(@) {
say( __say( ' ', @_) );
}
}
+sub __unique($) {
+ my ($input_arrayRef) = @_;
+ my %seen; keys(%seen) = scalar(@$input_arrayRef) / 2;
+ no warnings 'uninitialized';
+ foreach my $item (@$input_arrayRef) {
+ $seen{$item}++;
+ }
+ return %seen;
+}
+sub unique(@) {
+ my (@input_array) = @_;
+ my %seen = __unique(\@input_array);
+ return sort(keys(%seen));
+}
+sub countUnique(@) {
+ my (@input_array) = @_;
+ my %seen = __unique(\@input_array);
+ return map("$_($seen{$_})", sort(keys(%seen)));
+}
+
+
1;
0.4.2: Adds support for password-hashing, and makes a lot of changes to
the database schema. running db-setup.pl is required.
+0.4.3: Changes the SQL schema upgrading method to be safer and simpler.
+Changes the schema quite a bit even so. You MUST run the upgrade of
+0.4.2 before you can run the upgrade on 0.4.3. After 0.4.3 you should be
+able to skip versions if desired.
+
-----------------------------------------------------------------------
The db-setup.pl script should perform any database changes or
require constant; import constant(\%constants);
chdir $constants{PREFIX};
}
-use lib PREFIX;
-
+use lib PREFIX, "@{[PREFIX]}/CPAN";
+
use SrSv::Conf 'sql';
$dbh = DBI->connect('DBI:mysql:'.$sql_conf{'mysql-db'}, $sql_conf{'mysql-user'}, $sql_conf{'mysql-pass'},
--- /dev/null
+# whether ConnectServ should list JOIN/PART events
+joinpart = 0
# a unique value.
numeric = 123
+# This is for enabling IPv6 usage. You must set this to
+# 1 for true, or 0 for false.
+# IPv6 support isn't known to be bug-free, but it is running on SCnet.
+# If you see errors about 'Socket6' you have to disable this.
+ipv6 = 0
+
# You can set this to anything you want, it shows up in
# /whois and /links
info = SurrealServices
# If you use NeoStats, set this to the name of your NeoStats
# server. Otherwise, leave it commented out.
-#unsyncserver = neostats.example.com
+#unsyncserver = [neostats.example.com, denora.example.com]
# Uncomment this if you don't want any emails to be sent.
#nomail = 1
# a backup copy of your database. Keeping the backup for any period of time
# violates the point of hashing your passwords.
hashed-passwords = 0
+
+# Used for the special channel bantype 10
+
+# The assumption is that anything that starts with one of these prefixes
+# and a hyphen has a per-user ident that is maintained by a cookie and
+# should be reasonably persistent.
+#ban_webchat_prefixes = java|htIRC
+
+# Using tokens uses less bandwidth for processing commands
+# The TOK_FOO are shorter than CMD_FOO and can also allow faster hash matching
+# for command dispatch.
+# DON'T CHANGE UNLESS YOU'RE A DEVELOPER
+tokens = 1
+
+# If the queue size goes over queue_highwater,
+# a) low priority user commands are ignored and user is asked to come back later.
+# b) services notifies opers that this has occurred
+# c) svsop are exempt.
+# below are defaults
+#queue_highwater = 50
+
+# If the queue size goes over the queue_lowwater,
+# a notice/warning is put in the operchan (if defined) and the diagchan
+# However, this only applies to regular events (JOIN, NICK, et cetera),
+# not PRIVMSG in chans or to agents.
+#queue_lowwater = 30
+
+operchan = #opers
OPMZlineReason = Open proxy - see http://opm.blitzed.org/$
ProxyZlineTime = 21600
TorZlineReason = You are not permitted to connect to this network using anonymous proxies.
-TorServer = http://moria.seul.org:9031/tor/status/all
+TorServer = http://tor.noreply.org/tor/status/all
EnableTor = 1
CountryZlineReason = Due to persistent abuse, access to this network from $ is denied.
log-overrides = 0
# Default channel bot for all registrations
-#default-chanbot=ChanBot
+#default-chanbot = ChanBot
-# How many seconds to wait before assuming that a user
-# won't be coming back from a netsplit.
-# default is 5 minutes.
-# More than this hasn't been tested yet, but it should be ok.
-#old_user_age = 300
+# default mlock for channels when registered
+#default_channel_mlock = +nrt
+
+# Restricts channel registration to network staff
+# (anyone with helpop or higher)
+#chanreg-needs-oper = 0
# Allows you to add secondary names for the agents
botserv = undef
#adminserv = Secretary
#operserv = Bouncer
#hostserv = Butler
+
+# How long after signoff (or netsplit) a user entry is deleted/expired from.
+# the table in case they come back with the same userid & timestamp.
+# It also determines how long OS UINFO will be able to retrieve information
+# about a user assuming that no one has used that nick since.
+# The value is in seconds. Default is 300 (5 minutes)
+#old_user_age = 300
--- /dev/null
+pikachu
+charmander
+jynx
+blastoise
+typhlosion
+squirtle
+Raichu
+giratinaorigin
--- /dev/null
+# This sets the maximum amount of time, in seconds,
+# for pseudoclients to idle in a given channel,
+# before cycling over to a new pseudoclient
+#
+# Default is 14400 seconds, or 4 hours
+#
+idlemax = 14400
+
+# Same as above, except this sets the minimum
+# amount of time a pseudoclient will be in
+# a channel.
+#
+# Default is 3600 seconds, or 1 hour
+#
+idlemin = 3600
);
require constant; import constant(\%constants);
}
+
chdir PREFIX;
-use lib PREFIX;
+use lib PREFIX, "@{[PREFIX]}/CPAN";
our ($delete_db, $skip_backup, $auto_backup, $restore, $help);
BEGIN {
);
if($help) {
- print <<EOF;
+ print qq"
Options:
--delete Delete entire database
--skip-backup Don't nag about making a backup
--backup Make backup without upgrading
--restore FILE Restore the database from a backup
--help Show this message
-EOF
-
+";
+
exit 1;
}
}
use SrSv::Conf qw(main sql);
+use SrSv::DB::Schema;
BEGIN {
if($restore) {
}
}
-sub do_sql_file($) {
- my $file = shift;
- open ((my $SQL), $file) or die "$file: $!\n";
- my $sql;
- while(my $x = <$SQL>) { $sql .= $x unless $x =~ /^#/ or $x eq '\n'}
- foreach my $line (split(/;/s, $sql)) {
- $dbh->do($line);
- }
-}
-
unless($skip_backup) {
if($auto_backup or ask "Would you like to make a backup of your database: $sql_conf{'mysql-db'}? (Y/n) ") {
my @lt = localtime();
}
}
-print "Creating tables...\n";
-
$dbh->{RaiseError} = 0;
$dbh->{PrintError} = 0;
-do_sql_file("sql/services.sql");
+my ($ver) = check_schema();
+#print "$ver\n";
+if($ver == 0) {
+ print "Creating tables...\n";
+ do_sql_file("sql/services.sql");
+ upgrade_schema(0);
+} elsif($ver) {
+ upgrade_schema($ver);
+}
print "Updating chanperm...\n";
['INVITE', 4, 0],
['InviteSelf', 1, 0],
['TOPIC', 5, 0],
- ['UnbanSelf', 4, 0],
- ['UNBAN', 5, 0],
+ ['UnbanSelf', 2, 0],
+ ['UNBAN', 4, 0],
['VOICE', 2, 0],
['HALFOP', 3, 0],
['OP', 4, 0],
require constant; import constant(\%constants);
}
chdir PREFIX;
-use lib PREFIX;
-
+use lib PREFIX, "@{[PREFIX]}/CPAN";
+
use SrSv::Conf 'sql';
$dbh = DBI->connect('DBI:mysql:'.$sql_conf{'mysql-db'}, $sql_conf{'mysql-user'}, $sql_conf{'mysql-pass'},
-AdminServ Commands
+%BAdminServ%B allows services administrators or higher
+to list, modify, or look up users on the staff lists.
-SVSOP - Modify Services Operator
-WHOIS - Check rank of an individual
-STAFF - List all Services Ops
+Commands:
+ SVSOP Modify Services Operator
+ WHOIS Check rank of an individual
+ STAFF List all Services Ops
%BAdminServ STAFF%B lists all services staff-members, in rank
-order.
\ No newline at end of file
+order.
+
+Syntax: %BSTAFF%B
-Modify Services Operator
+%BAdminServ SVSOP%B modifies the rank of a user and lists
+the services operators.
-Syntax: SVSOP <ADD|DEL|LIST> <nick> <H|O|A|R>
+Syntax: %BSVSOP%B <%UADD|DEL|LIST%U> %Unick%U <%UH|O|A|R%U>
Services Access:
-
-H HelpOp
-O [Services] Operator
-A [Services] Admin
-R Services Root
+ H HelpOp
+ O [Services] Operator
+ A [Services] Admin
+ R Services Root
--- /dev/null
+%BAdminServ WHOIS%B displays the services rank of a given user.
+
+Syntax: %BWHOIS%B %Unick%U
%BBotServ ACT%B allows you to make a bot perform an action in a channel.
-Syntax: %BACT%B %U#channel%U %Uaction%U
+Syntax: %BACT%B %U#channel%U <%Uaction%U>
%BBotServ ADD%B allows you to create a new services bot.
-Syntax: %BADD%B %Unick%U %Uident%U %Uvhost%U %Urealname%U
+Syntax: %BADD%B <%Unick%U> <%Uident%U> <%Uvhost%U> <%Urealname%U>
%BBotServ ASSIGN%B allows you to assign a bot to a channel. Once assigned,
the bot will join the channel and accept commands.
-Syntax: %BASSIGN%B %U#channel%U %Ubot%U
+Syntax: %BASSIGN%B %U#channel%U <%Ubot%U>
%BBotServ SAY%B allows you to make a bot send a message to a channel.
-Syntax: %BSAY%B %U#channel%U %Umessage%U
+Syntax: %BSAY%B %U#channel%U <%Umessage%U>
Deaf bots will not be able to be used for
any kind of badword kicking.
-Syntax: %BSET%B %Ubot%U %Uflag%U %UOn/Off%U
+Syntax: %BSET%B %Ubot%U <%Uflag%U> <%UON/OFF%U>
!down Removes all channel status.
!invite Invites a user to the channel.
!ban Bans a user or mask from the channel.
+ !banlist Lists all bans in the channel, suitable for using the
+ numbers with !unban
+ !qban Places a quiet ban on the user in the channel.
+ !nban Places a nick-change ban on the user in the channel.
!kick Kicks a user from the channel.
!kickban Kicks and bans a user from the channel.
!kickmask Kicks users matching a mask from the channel.
!dice Rolls dice, !d 2d4 rolls 2 4 sided dice.
!mode Sets modes in a channel.
!resync Gives everyone the precise chan-ops they're supposed to have.
+ !topic Sets the topic of the channel.
+
+ !abbreviations Shows all short command aliases.
+ !abbrev
+ !abbrevs
Commands to set channel status:
!voice !halfop !op !admin
--- /dev/null
+Commands:
+ !b !ban
+ !k !kick
+ !kb !kickban
+ !kbm !kickbanmask
+ !km !kickmask
+ !kbmask !kickbanmask
+ !d !dice
+ !m !mode
+ !blist !banlist
+ !t !topic
%BChanServ%B allows you to register and control various aspects of
channels. ChanServ can prevent malicious users from "taking
over" channels by limiting who is allowed channel operator
-priviliges.
-
+privileges.
+
Commands:
REGISTER Register a channel
SET Change various channel configuration settings
INFO Information about a channel
DROP Drop a registered channel
MODE Change channel modes.
-
+
Commands to manipulate access lists:
CF SOP AOP HOP VOP UOP AUTH
-
+
Commands to change or check channel status:
VOICE OP HALFOP PROTECT UP
DEVOICE DEOP DEHALFOP DEPROTECT DOWN
WHY COUNT ALIST RESYNC
-
+
+Commands for moderating a channel's users
+ KICK KICKBAN KICKMASK KICKBANMASK
+ BAN UNBAN BANLIST TEMPBAN
+
+Network Admin Commands:
+ GETKEY CLOSE DRONE
+
Other available commands:
- DICE INVITE GETKEY CLOSE COPY
- CLEAR WELCOME DRONE KICKMASK
- KICK KICKBAN MLOCK KICKBANMASK
-
+ DICE JOIN INVITE
+ WELCOME CLEAR MLOCK
+ COPY TOPIC TOPICAPPEND
+
Note that channels will be dropped after 21 days of inactivity.
For more help on a specific command, type: %B/cs help%B %Ucommand%U
%BChanServ ADMIN%B allows you to set channel-admin mode on
either yourself or on other people in a channel.
-Syntax:
- ADMIN <channel> [nick [nick ...]]
- ADMIN <channel> [channel [channel]]
+Syntax: %BADMIN%B %U#channel%U [%Unick%U [%Unick%U ...]]
+ %BADMIN%B %U#channel%U [%Uchannel%U [%Uchannel%U]]
If a user on the AutoKick list attempts to join the channel,
ChanServ will ban that user from the channel, then kick the user.
-Syntax: %BAKICK%B %U#channel%U %BADD%B %Unick/mask%U %Ureason%U
- %BAKICK%B %U#channel%U %BDEL%B %Unick/mask/list%U
+Syntax: %BAKICK%B %U#channel%U %BADD%B <%Unick/mask%U> [%Ureason%U]
+ %BAKICK%B %U#channel%U %BDEL%B <%Unick/mask/list%U>
%BAKICK%B %U#channel%U %BLIST%B
The %BAKICK ADD%B command adds the given nick or hostmask to
The %BAKICK LIST%B command displays the AutoKick list.
#or
#optionally only those AutoKick entries which match the given
-#mask.
+#mask.
+
+The reason is used when kicking and is visible in AKICK LIST. If
+the reason contains a '|' character everything after it does not
+appear in bans placed by an AutoKick; but does appear in AKICK
+LIST.
Users on this list are given op status upon joining
the channel.
-Syntax: %BAOP%B %U#channel%U %BADD%B %Unick%U
- %BAOP%B %U#channel%U %BDEL%B %Unick%U
+Syntax: %BAOP%B %U#channel%U %BADD%B <%Unick%U>
+ %BAOP%B %U#channel%U %BDEL%B <%Unick%U>
%BAOP%B %U#channel%U %BLIST%B [%Umask%U]
%BAOP%B %U#channel%U %BWIPE%B
%BChanServ AUTH%B allows channel SOps to delete old/stale
entries from the pending access list.
-Syntax: %BAUTH %U<#channel>%U <LIST|DELETE> %U[number|name]%U%B
+Syntax: %BAUTH%B %U#channel%U <%BLIST|DELETE%B> [%Unumber|name%U]
Examples:
/msg ChanServ AUTH #SurrealChat LIST
%BChanServ BAN%B Tells ChanServ to set a ban on a person or
mask. It can also remove bans, if you prefix the ban with a -
-Syntax: %BBAN %U#channel%U <nick|mask>%B
+Syntax: %BBAN%B %U#channel%U <%Bnick|mask%B>
By default limited to %BHOP%B
--- /dev/null
+%BChanServ BANLIST%B asks ChanServ for the list of bans in
+a channel.
+
+Syntax: %BBANLIST%B %U#channel%U
%BChanServ CF%B maintains the cofounder list for a channel.
Users on this list are allowed to do anything the founder can do.
-Syntax: %BCF%B %U#channel%U %BADD%B %Unick%U
- %BCF%B %U#channel%U %BDEL%B %Unick%U
+Syntax: %BCF%B %U#channel%U %BADD%B <%Unick%U>
+ %BCF%B %U#channel%U %BDEL%B <%Unick%U>
%BCF%B %U#channel%U %BLIST%B [%Umask%U]
%BCF%B %U#channel%U %BWIPE%B
channel becomes successor, in case the founder nick is
dropped.
-Syntax: %BCLOSE %U#channel%U Reason%B
+Syntax: %BCLOSE%B %U#channel%U <%Ureason%U>
Requires SERVOP.
May only copy one particular xOp/rank list.
* LEVELS
-Syntax:
- %BCOPY #chan1 [%Utype [rank]%U] #chan2%B
+Syntax: %BCOPY%B %U#chan1%U [%Utype [rank]%U] %U#chan2%%U
Examples:
COPY #chan1 #chan2
For more information, see ChanServ CLOSE.
-Syntax: %BDRONE %U#channel%U Reason%B
+Syntax: %BDRONE%B %U#channel%U <%Ureason%U>
Requires SERVOP.
%BChanServ HALFOP%B allows you to set halfop mode on either
yourself or on other people in a channel.
-Syntax:
- HALFOP <channel> [nick [nick ...]]
- HALFOP <channel> [channel [channel]]
+Syntax: %BHALFOP%B %U#channel%U [%Unick%U [%Unick%U ...]]
+ %BHALFOP%B %U#channel%U [%Uchannel%U [%Uchannel%U]]
Users on this list are given half-op status upon joining
the channel.
-Syntax: %BHOP%B %U#channel%U %BADD%B %Unick%U
- %BHOP%B %U#channel%U %BDEL%B %Unick%U
+Syntax: %BHOP%B %U#channel%U %BADD%B <%Unick%U>
+ %BHOP%B %U#channel%U %BDEL%B <%Unick%U>
%BHOP%B %U#channel%U %BLIST%B [%Umask%U]
%BHOP%B %U#channel%U %BWIPE%B
--- /dev/null
+%BChanServ JOIN%B requests ChanServ to join you to the
+channel. If you have channel access, the bot will also give
+you an invite, to bypass any bans or other restrictive
+channel modes.
+
+If you are not allowed to join the channel, you will not
+join.
+
+Syntax: %BJOIN%B %U#channel%U [%U#channel%U [%U#channel%U]]
%BChanServ KICK%B allows you to kick a user from a channel.
-Syntax: %BKICK%B %U#channel%U %Unick%U [%Ureason%U]
+Syntax: %BKICK%B %U#channel%U <%Unick%U> [%Ureason%U]
%BChanServ KICKBAN%B allows you to kick and ban a user from
a channel.
-Syntax: %BKICKBAN%B %U#channel%U %Unick%U [%Ureason%U]
+Syntax: %BKICKBAN%B %U#channel%U <%Unick%U> [%Ureason%U]
hostmask from the channel. It does not affect users with
channel access.
-Syntax: %BKICKBANMASK%B %U#channel%U %Umask%U [%Ureason%U]
+Syntax: %BKICKBANMASK%B %U#channel%U <%Umask%U> [%Ureason%U]
hostmask from the channel. It does not affect users with
channel access.
-Syntax: %BKICKMASK%B %U#channel%U %Umask%U [%Ureason%U]
+Syntax: %BKICKMASK%B %U#channel%U <%Umask%U> [%Ureason%U]
%BChanServ LEVELS%B allows you to adjust the minimum access levels
required to do certain commands.
-Syntax: %BLEVELS%B %U#channel%U %BSET%B %Ucommand%U %Ulevel%U
- %BLEVELS%B %U#channel%U %BRESET%B %Ucommand%U
+Syntax: %BLEVELS%B %U#channel%U %BSET%B <%Ucommand%U> <%Ulevel%U>
+ %BLEVELS%B %U#channel%U %BRESET%B <%Ucommand%U>
%BLEVELS%B %U#channel%U %BLIST%B
%BLEVELS%B %U#channel%U %BCLEAR%B
(Channels with the PRIVATE option set are not listed, of
course.)
-Syntax: %BLIST %Umask%U%B
+Syntax: %BLIST%B <%Umask%U>
%BChanServ MLOCK%B allows you to lock channel modes either
on or off.
-Syntax: %BMLOCK%B %U#channel%U %B<SET|ADD|DEL>%B %Umodes%U
+Syntax: %BMLOCK%B %U#channel%U <%BADD|DEL|SET|RESET%B> %Umodes%U
The %Umodes%U parameter is constructed exactly the same way as
a %B/MODE%B command; that is, modes preceded by a %B+%B are locked
%BMLOCK%B %U#channel%U %BDEL%B %UQ%U
Removes Q from your mlock, it may be +Q or -Q.
+ %BMLOCK%B %U#channel%U %BRESET%B
+ Resets the mode lock to default.
+
%BMLOCK%B %U#channel%U %BSET%B %U+nt-iklps%U
+ DON'T USE %USET%U. USE %UADD%U OR %UDEL%U.
Forces modes n and t on, and modes i, k, l, p, and
s off. Mode m (and others) are left free to be either
on or off.
%BMLOCK%B %U#channel%U %BSET%B %U+knst-ilmp%U %Umy-key%U
+ DON'T USE %USET%U. USE %UADD%U OR %UDEL%U.
Forces modes k, n, s, and t on, and modes i, l, m,
and p off. Also forces the channel key to be
"my-key".
command does, but allows you to do so if you're not currently
opped in the channel.
-Syntax: %BMODE%B %U#channel%U %U+modes-modes%U %U%B[%Bparams%B]%B%U
+Syntax: %BMODE%B %U#channel%U <%U+modes-modes%U> [%Uparams%U]
Example: MODE #Support +tn
MODE #Support +ootn hAtbLaDe XYZ
%BChanServ OP%B allows you to set channel op mode on either
yourself or on other people in a channel.
-Syntax:
- OP <channel> [nick [nick ...]]
- OP <channel> [channel [channel]]
+Syntax: %BOP%B %U#channel%U [%Unick%U [%Unick%U ...]]
+ %BOP%B %U#channel%U [%Uchannel%U [%Uchannel%U]]
This is actually implemented via CS CLEAR <#chan> OPS, and a
CS UP #chan <all users>
-Syntax: %BRESYNC%B %U#channel [#channel [#channel]]%U
+Syntax: %BRESYNC%B %U#channel%U [%U#channel%U [%U#channel%U]]
Don't use it.
AUTOVOICE Voices everyone who joins the channel.
BANTYPE Selects the ban-type to be used for KICKBAN and AKICKs
-
+ TOPICLOCK Restricts who can change the topic in the channel.
+ NOCLONES Bans people who bring clones into the channel.
+ BANTIME Time until bans are automatically removed.
Oper only flags:
HOLD Prevent channel from expiring
FREEZE Suspend access in this channel
--- /dev/null
+%BChanServ SET BANTIME%B sets the default ban time for /cs (kick)ban and /cs tempban.
+Default is 0 (permanent unless manually removed)
+Examples:
+/cs set #pokemonlake bantime +24h
+/cs set #pokemondeluge bantime +12h
+Syntax: %BSET%B %U#channel%U %BBANTIME%B +<%Utime%U>
7 - nick!*@host.domain
8 - nick!*user@*.domain
9 - nick!*@*.domain
+ 10 - cross btwn 2 and 3, depending on if is a java-abcd1 ident or not
+
+Syntax: %BSET%B %U#channel%U %BBANTYPE%B <%Unumber%U>
%BChanServ SET DESC%B sets the description for the channel,
which can be seen with the %BChanServ INFO%B command.
-Syntax: %BSET%B %U#channel%U %BDESC%B %Udescription%U
+Syntax: %BSET%B %U#channel%U %BDESC%B <%Udescription%U>
You must be the current founder of the channel, and the new
founder must have a registered nick.
-Syntax: %BSET%B %U#channel%U %BFOUNDER%B %Unick%U
+Syntax: %BSET%B %U#channel%U %BFOUNDER%B <%Unick%U>
Once you have used this command, you will be automatically
moved to the channel's co-founder list; you may then remove
--- /dev/null
+%BChanServ SET FREEZE%B sets whether the given channel
+will be suspended, preventing users in that channel
+from being granted their access.
+
+Syntax: %BSET%B %U#channel%U %BFREEZE%B <%UON/OFF%U>
+
+Requires SERVOP.
-%BChanServ SET HOLD%B Sets whether the given channel will
+%BChanServ SET HOLD%B sets whether the given channel will
expire. Setting this to ON prevents the channel from
expiring.
-Syntax: ChanServ SET #channel HOLD <ON/OFF>
+Syntax: %BSET%B %U#channel%U %BHOLD%B <%UON/OFF%U>
+
+Requires SERVOP.
in the channel. Users may still use the UP command to gain
chanop.
-Syntax: %BSET #chan NEVEROP %U{ON|OFF}%U%B
+Syntax: %BSET%B %U#channel%U %BNEVEROP%B <%UON/OFF%U>
--- /dev/null
+%BChanServ SET NOCLONES%B Kicks and bans users who bring clones (multiple connections) to a channel.
+Users in the channel's access lists are excempt.
+
+Syntax: %BSET%B %U#channel%U %BNOCLONES%B <%UON/OFF%U>
%BChanServ SET OPGUARD%B makes ChanServ strictly
control channel status.
-Syntax: %BSET%B %U#channel%U %BOPGUARD%B %UON/OFF%U
+Syntax: %BSET%B %U#channel%U %BOPGUARD%B <%UON/OFF%U>
When OpGuard is set to ON, only people with the
appropriate permission will be allowed to grant
%BChanServ SET PASSWORD%B changes the password of a channel.
-Syntax: %BSET%B %U#channel%U %BPASSWORD%B %Upassword%U
+Syntax: %BSET%B %U#channel%U %BPASSWORD%B <%Upassword%U>
Note: Channel passwords cannot be used; this command
exists only for completeness.
identified to NickServ, but may make channel takeovers
easier.
-Syntax: SET <channel> SPLITOPS {ON|OFF}
+Syntax: %BSET%B %U#channel%U %BSPLITOPS%B <%UON/OFF%U>
%BChanServ SET SUCCESSOR%B changes the successor of a channel.
The new successor must have a registered nick.
-Syntax: %BSET%B %U#channel%U %BSUCCESSOR%B %Unick%U
+Syntax: %BSET%B %U#channel%U %BSUCCESSOR%B <%Unick%U>
The channel successor will be made founder in case the original
founder's nick is expired or dropped. A channel with no
--- /dev/null
+Syntax: SET #CHANNEL TOPICLOCK <UOP|VOP|AOP|SOP|CFOUNDER|FOUNDER|OFF>
+
+Enables or disables the TOPICLOCK option for a channel.
+When TOPICLOCK is set, ChanServ will not allow the
+channel topic to be changed unless access permits.
AKick - Add/Delete/Move/Wipe
VOp/HOp/AOp/SOp/CoFounder - Add/Delete/Move/Wipe
BotSay - Usage
+
+Syntax: %BSET% %U#channel%U %BVERBOSE%B <%UON/OFF%U>
This flag probably doesn't do what you think it does.
Don't use it.
-Syntax: %BSET #chan WelcomeInChan %U{ON|OFF}%U%B
+Syntax: %BSET%B %U#channel%U %BWelcomeInChan%B <%UON/OFF%U>
%BChanServ SOP%B maintains the super-op list for a channel.
-Syntax: %BSOP%B %U#channel%U %BADD%B %Unick%U
- %BSOP%B %U#channel%U %BDEL%B %Unick%U
+Syntax: %BSOP%B %U#channel%U %BADD%B <%Unick%U>
+ %BSOP%B %U#channel%U %BDEL%B <%Unick%U>
%BSOP%B %U#channel%U %BLIST%B [%Umask%U]
%BSOP%B %U#channel%U %BWIPE%B
--- /dev/null
+%BChanServ TEMPBAN%B Tells ChanServ to set a ban on a person or
+mask for a set amount of time. The ban will be removed automatically after that time has elapsed.
+
+Syntax: %BTEMPBAN%B %U#channel%U %B<nick|mask>%B <%B+TIME%B>
+
+Examples: TEMPBAN #pokemoncrater erry +1d Enough already!
+TEMPBAN #pokemonlake mario121 Marcus +7d
+
+By default limited to %BHOP%B
--- /dev/null
+%BChanServ TOPIC%B sets, or unsets, the current topic of the channel.
+To unset the topic, use NONE as the message.
+
+Syntax: %BTOPIC%B %U#channel%U <message|NONE>
--- /dev/null
+%BChanServ TOPICAPPEND%B appends a phrase to the current topic, or sets the topic if no topic is set yet.
+
+Syntax: %BTOPICAPPEND%B %U#channel%U <message>
+Examples: /cs TOPICAPPEND #erry COOKIES!
+-!- Eustace has changed the topic of #erry to: SLINKIES | COOKIES!
--- /dev/null
+%BChanServ TOPICAPPEND%B prepends a phrase to the current topic, or sets the topic if no topic is set yet.
+
+Syntax: %BTOPICPREPEND%B %U#channel%U <message>
+Examples: /cs TOPICPREPEND #erry COLORSS!!!
+-!- Eustace has changed the topic of #erry to: COLORSS!!! | SLINKIES | COOKIES!
preventing you or another person from entering the given
channel, or remove particular ban-masks.
-Syntax: %BUNBAN %U#channel%U [nick|mask]%B
+Syntax: %BUNBAN%B %U#channel%U [%Unick|mask%U]
By default limited to %BAOP%B
%BChanServ UOP%B maintains the user list for a channel.
-Syntax: %BUOP%B %U#channel%U %BADD%B %Unick%U
- %BUOP%B %U#channel%U %BDEL%B %Unick%U
+Syntax: %BUOP%B %U#channel%U %BADD%B <%Unick%U>
+ %BUOP%B %U#channel%U %BDEL%B <%Unick%U>
%BUOP%B %U#channel%U %BLIST%B [%Umask%U]
%BUOP%B %U#channel%U %BWIPE%B
%BChanServ VOICE%B allows you to set channel-voice mode on either
yourself or on other people in a channel.
-Syntax:
- VOICE <channel> [nick [nick ...]]
- VOICE <channel> [channel [channel]]
+Syntax: %BVOICE%B %U#channel%U [%Unick%U [%Unick%U ...]]
+ %BVOICE%B %U#channel%U [%Uchannel%U [%Uchannel%U]]
Users on this list are given voice status upon joining
the channel.
-Syntax: %BVOP%B %U#channel%U %BADD%B %Unick%U
- %BVOP%B %U#channel%U %BDEL%B %Unick%U
+Syntax: %BVOP%B %U#channel%U %BADD%B <%Unick%U>
+ %BVOP%B %U#channel%U %BDEL%B <%Unick%U>
%BVOP%B %U#channel%U %BLIST%B [%Umask%U]
%BVOP%B %U#channel%U %BWIPE%B
list. The contents of this list will be sent to every user who
joins your channel.
-Syntax: %BWELCOME%B %U#channel%U %BADD%B %Umessage%U
- %BWELCOME%B %U#channel%U %BDEL%B %Unumber%U
+Syntax: %BWELCOME%B %U#channel%U %BADD%B <%Umessage%U>
+ %BWELCOME%B %U#channel%U %BDEL%B <%Unumber%U>
%BWELCOME%B %U#channel%U %BLIST%B
The %BWELCOME ADD%B command adds a message to the welcome list.
%BChanServ WHY%B tells what status a user has in a channel.
-Syntax: %BWHY%B %U#channel%U %Unick%U
+Syntax: %BWHY%B %U#channel%U [%Unick%U [%Unick%U ...]]
using SET <nick> <ident>@<hostmask> set idents for users as well
as vhosts.
-Syntax: %BSET%B %Unick%U %Uvhost%U
+Syntax: %BSET%B %Unick%U <%Uvhost%U>
%BMemoServ CSEND%B allows you to send a channel memo
to users of a certain rank or higher.
-Syntax: %BCSEND%B %U#channel%U %Urank%U %Umessage%U
+Syntax: %BCSEND%B %U#channel%U <%Urank%U> <%Umessage%U>
Valid ranks include: UOP, VOP, HOP, AOP, SOP, CF, FOUNDER
%BMemoServ DEL%B deletes a memo, a series of memos, or all memos.
-Syntax: %BDEL%B %Unumber%U
- %BDEL%B %Unumber1-number2%U
- %BDEL%B %Unumber1..number2%U
+Syntax: %BDEL%B <%Unumber%U>
+ %BDEL%B <%Unumber1-number2%U>
+ %BDEL%B <%Unumber1..number2%U>
%BDEL ALL%B
ignored person will be notified that they are on your ignore
list and you do not wish to receive any memos from them.
-%BSyntax: IGNORE [ADD|DEL|LIST] [<nick>]%B
+Syntax: %BIGNORE%B <%UADD|DEL|LIST%U> [<%Unick%U>]
This is a good way to avoid memos from people who spam you.
They can get around an ignore by registering a different
-Syntax: %BREAD {%Bnum%B|%BLAST%B}%B
+%BMemoServ READ%B displays a specified memo that you have
+received.
+
+Syntax: %BREAD <%Unum|LAST%U>
Sends you the text of memo number %Bnum%B, or of the last
(i.e. most recently received) memo if LAST is given
%BMemoServ SEND%B allows you to send a memo to a nick or channel.
-Syntax: %BSEND%B %Unick/#chan%U %Umessage%U
+Syntax: %BSEND%B <%Unick/#chan%U> <%Umessage%U>
RELEASE Releases your nick from services custody
INFO Get information about a nick
DROPGROUP Delete a registered nickname and all aliases
-
+
LINK Make an alias of your nick
UNLINK Remove an alias
DROP Same as UNLINK
CHGROOT Change your root nick
Additional Commands:
- GLIST ALIST WATCH SILENCE ACC SEEN
- LOGOUT LIST AUTH AUTHCODE SENDPASS PROFILE
+ GLIST ALIST WATCH SILENCE ACC SEEN AJOIN LISTEMAIL
+ LOGOUT LIST AUTH AUTHCODE SENDPASS PROFILE
%BNOTICE:%B This service is intended to provide a way for IRC users to
ensure their identity is not compromised. It is NOT intended to
4 - Identified via access list
5 - Forbidden Nickname
-Syntax: %BACC%B <%Bnickname%B>
+Syntax: %BACC%B %Unickname%U
--- /dev/null
+Syntax: %BAJOIN%B [%Unick%U] %BADD%B <%Uchannel%U>
+ %BAJOIN%B [%Unick%U] %BDEL%B <%Uchannel|entry-nr|list%U>
+ %BAJOIN%B [%Unick%U] %BLIST%B [%Umask|list%U]
+ %BAJOIN%B [%Unick%U] %BJOIN%B
+ %BAJOIN%B [%Unick%U] %BCLEAR%B
+ %BAJOIN%B [%Unick%U] %BWIPE%B
+
+Maintains the %BAutoJoin list%B for nick group.
+If a user identifies to his nickname, he will
+automatically join the listed channels.
+
+The %BAJOIN ADD%B command adds the given channel
+to the AutoJoin list.
+
+The %BAJOIN DEL%B command removes the given channel
+from the AutoJoin list. If a list of entry numbers is given,
+those entries are deleted.
+
+The %BAJOIN LIST%B command displays the AutoJoin list.
+If a wildcard mask is given, only those entries matching the
+mask are displayed. If a list of entry numbers is given, only
+those entries are shown.
+
+The %BAJOIN JOIN%B attempts to join you to all of the channels
+in your list.
+NOTE: %BAJOIN%B does not [attempt to] bypass bans, chmode +i,
+or any other such thing.
+
+The %BAJOIN WIPE%B command clears all entries on the
+AutoJoin list.
REJECT - Same as decline
LIST - List auth requests.
-Syntax: AUTH [nick] <LIST|ACCEPT|DECLINE> [num|chan]
+Syntax: AUTH [%Unick%U] <%ULIST|ACCEPT|DECLINE%U> [%Unum|chan%U]
If you do not want to be added to that channel list, use decline or reject.
Demotions are handled by deleting the target's access, so they may accept
More information on its use should be in the email you receive.
-Syntax: %BAUTHCODE%B %Unickname%U %Ucode%U [%Unewpassword%U]
+Syntax: %BAUTHCODE%B %Unickname%U <%Ucode%U> [%Unewpassword%U]
%BNickServ DROP%B allows you to relinquish a previously registered
nickname.
-Syntax: %BDROP%B %Unick%U %Upassword%U
+Syntax: %BDROP%B %Unick%U <%Upassword%U>
%BNickServ DROPGROUP%B allows you to delete a whole group of
nicks/aliases
-Syntax: %BDROPGROUP%B %Unick%U %Upassword%U
+Syntax: %BDROPGROUP%B %Unick%U <%Upassword%U>
More information on its use should be in the email you receive.
-Syntax: %BEMAILREG%B %Unickname%U %Ucode%U
+Syntax: %BEMAILREG%B %Unickname%U <%Ucode%U>
Typically, this happens when your Internet connection goes down
while you're on IRC.
-Syntax: %BGHOST%B %Unickname%U [%Upassword%U]
+Syntax: %BGHOST%B %Unick%U [%Upassword%U]
#
#In order to use the GHOST command for a nick, your current
#address as shown in /WHOIS must be on that nick's access
identifying, and will use GHOST if the target nick is
currently online.
-Syntax: GIDENTIFY <nick> <password>
+Syntax: %BGIDENTIFY%B %Unick%U <%Upassword%U>
Examples:
- IDENTIFY n00b
- IDENTIFY Soulja soulseeker
- ID bob thebuilder
+ GIDENTIFY Soulja soulseeker
+ GID bob thebuilder
The password should be the same one you sent with the
%BREGISTER%B command.
-Syntax: %BIDENTIFY%B [%Unick%U] %Upassword%U
+Syntax: %BIDENTIFY%B [%Unick%U] <%Upassword%U>
Examples:
IDENTIFY n00b
nicknames, such as the nick's owner, last seen address and
time, and nick options.
-Syntax: %BINFO%B %Unickname%U [%Unickname%U ...]
+Syntax: %BINFO%B %Unick%U [%Unick%U ...]
nickname. Linked nicknames share everything from access
lists and settings to memos.
-Syntax: %BLINK%B %Unick%U %Upassword%U
+Syntax: %BLINK%B %Unick%U <%Upassword%U>
This command does NOT support linking two groups together.
--- /dev/null
+%BNickServ LISTEMAIL%B is used by opers to find registered nicks
+whose email addresses match a specified pattern.
+
+Syntax: %BLISTEMAIL%B %Uemail@address.tld%U
%BNickServ PROFILE%B stores information about you for others
to read.
-Syntax: %BPROFILE READ%B %Unickname%U [%Unickname%U ...]%B
+Syntax: %BPROFILE READ%B <%Unick%U> [%Unick%U ...]
%BPROFILE%B [%Unick%U] %BSET%B %Uitem%U %Udata%U
%BPROFILE%B [%Unick%U] %BDEL%B %Uitem%U
%BPROFILE%B [%Unick%U] %BWIPE%B
jupes the nick for one minute. To use it yourself, use
%BNS RELEASE%B, then change your nick, OR use %BNS SIDENTIFY%B
-Syntax: %BRECOVER%B %Unickname%U [%Upassword%U]
+Syntax: %BRECOVER%B %Unick%U [%Upassword%U]
This command also identifies you to your nick if you are not
already. If you are already identified, the password is
%BNickServ REGISTER%B allows you to reserve a particular nickname for
your own use and prove your identity using a password.
-Syntax: %BREGISTER <password> <e-mail>%B
+Syntax: %BREGISTER%B <%Upassword%U> <%Ue-mail%U>
%BNOTICE:%B The email address is %BNOT%B optional and you are strongly
discouraged from using a fake address, as this will make it impossible
will hold a nickname that is used without authorization for one
minute; this command releases it sooner.
-Syntax: %BRELEASE%B %Unickname%U [%Upassword%U]
+Syntax: %BRELEASE%B %Unick%U [%Upassword%U]
without authorization from you.
VACATION Extend the time your nick will last before expiring.
ROOT Change the root nick for your nickgroup.
+ UMODE Set user modes to be added or removed upon identifying
Oper only flags:
- HOLD Prevent channel from expiring
- FREEZE Suspend access in this channel
+ HOLD Prevent nickname from expiring
+ FREEZE Suspend access to this nickname
EMAILREG If enabled, forces user to revalidate their email address.
If disabled, force validates their email address.
NickServ SET AUTH enables selective Channel Rank acceptance.
See HELP NickServ AUTH for more information.
-Syntax: %BSET [nick] AUTH %U{ON|OFF}%U%B
+Syntax: %BSET%B [%Unick%U] %BAUTH%B <%UON|OFF%U>
the nick. This address will be displayed whenever someone
requests information on the nick with the INFO command.
-Syntax: %BSET [nick] EMAIL %Uaddress%U%B
+Syntax: %BSET%B [%Unick%U] %BEMAIL%B <%Uaddress%U>
The %BHIDEMAIL%B command will hide your email from INFO
requests.
If %BHIDEMAIL%B is given, your email will no longer be hidden
-and will be visible to all in INFO requests.
\ No newline at end of file
+and will be visible to all in INFO requests.
displayed when joining a channel that you have sufficient
access to and has GREET enabled.
-Syntax: SET GREET <NONE|message>
+Syntax: %BSET%B GREET%B <%UNONE|message%U>
NONE will remove/unset your greet.
%BNickServ SET HIDEMAIL%B hides your email address from users.
-Syntax: %BSET [nick] HIDEMAIL%B %UON/OFF%U
+Syntax: %BSET%B [%Unick%U] %BHIDEMAIL%B <%UON|OFF%U>
If %BHIDEMAIL%B is disabled, your email will no longer be
hidden and will be visible in your NickServ INFO listing.
-%BNickServ SET HOLD%B Sets whether the given nick will
+%BNickServ SET HOLD%B sets whether the given nick will
expire. Setting this to ON prevents the nick from
expiring.
-Syntax: NickServ SET [nick] HOLD <ON/OFF>
+Syntax: %BSET%B [%Unick%U] %BHOLD%B <%UON/OFF%U>
channel status upon channels. You may still use the UP command
to gain your status.
-Syntax: %BSET [nick] NEVEROP %U{ON|OFF}%U%B
+Syntax: %BSET%B [%Unick%U] %BNEVEROP%B <%UON|OFF%U>
%BNickServ SET NOACC%B Prevents other people from adding
you to channel access lists.
-Syntax: %BSET [nick] NOACC %U{ON|OFF}%U%B
+Syntax: %BSET%B [%Unick%U] %BNOACC%B <%UON|OFF%U>
nick. This does not prevent you from sending memos, it will
only block receieving memos to you from others.
-Syntax: %BSET [nick] NOMEMO %U{ON|OFF}%U%B
+Syntax: %BSET%B [%Unick%U] %BNOMEMO%B <%UON|OFF%U>
%BNickServ SET PASSWD%B Changes the password used to
identify you as the nick's owner
-Syntax: %BSET [nick] PASSWD%B %Upassword%U
+Syntax: %BSET%B [%Unick%U] %BPASSWD%B %Upassword%U
%BNickServ SET PROTECT%B allows you to control the extent to which
your nick will be protected from unauthorized use.
-Syntax: %BSET [nick] PROTECT (OFF|ON|HIGH|KILL)%B
+Syntax: %BSET%B [%Unick%U] %BPROTECT%B <%UOFF|ON|HIGH|KILL%U>
With PROTECT OFF, anyone may use your nick without authorization.
%BNickServ SET UMODE%B sets the umodes that NickServ will set
on you when you identify.
-Syntax: %BSET [nick] UMODE %U<+modes-modes|none>%U%B
+Syntax: %BSET%B [%Unick%U] %BUMODE%B <%U+modes-modes|none%U>
The flag is cleared on your next identify, and you will not be
able to use it again until %Eint($services::conf{'vacationexpire'}/3)%E days have passed.
-Syntax: %BSET%B [%Unick%U] %BVACATION%B %Uon%U/%Uoff%U
+Syntax: %BSET%B [%Unick%U] %BVACATION%B <%UON|OFF%U>
It will automatically release an enforced nick, but it will
not ghost or recover your nick if another user is using it.
-Syntax: SIDENTIFY <nick> <password>
+Syntax: %BSIDENTIFY%B %Unick%U <%Upassword%U>
Examples:
SIDENTIFY n00b
list. Users on your silence list will not be able to send you private
messages.
-Syntax: %BSILENCE ADD%B %Unick!ident@host%U [%U+expiry%U] [%Ucomment%U]
- %BSILENCE ADD%B %Unick%U [%U+expiry%U] [%Ucomment%U]
- %BSILENCE DEL%B %Unick!ident@host%U
+Syntax: %BSILENCE ADD%B <%Unick!ident@host%U> [%U+expiry%U] [%Ucomment%U]
+ %BSILENCE ADD%B <%Unick%U> [%U+expiry%U] [%Ucomment%U]
+ %BSILENCE DEL%B <%Unick!ident@host%U>
%BSILENCE LIST%B
The %BSILENCE ADD%B command adds a nick or hostmask to your silence list.
The %BSILENCE LIST%B command displays your silence list.
+%BExamples:%B
+ /ns silence add erry +24h
+ /ns silence del 3
+ /ns silence add candyland101 +365d
+
%BCAVEATS:%B You cannot have more than 32 silence entries.
+Use of an appropriate expiration is highly recommended.
%BNickServ UNLINK%B allows you to delete a linked nickname.
-Syntax: %BUNLINK%B %Unick%U %Upassword%U
+Syntax: %BUNLINK%B %Unick%U <%Upassword%U>
%BNickServ WATCH%B allows you to view and modify your NickServ watch list.
You will be notified when a user on your watch list connects to IRC.
-Syntax: %BWATCH ADD%B %Unick/mask%U
- %BWATCH DEL%B %Unick/mask%U
+Syntax: %BWATCH ADD%B <%Unick/mask%U>
+ %BWATCH DEL%B <%Unick/mask%U>
%BWATCH LIST%B
The %BWATCH ADD%B command adds the specified nick or hostmask to your
CLONES Lists and/or manipulates clones.
Similar to LONERS.
MASSKILL Alias for CLONES KILL.
+ KILLNEW List/kill/uinfo/kline newly connected users.
%BOperServ CHANKILL%B adds a G:line for every user in a channel,
IRCOps excepted.
-Syntax: %BCHANKILL%B %U#channel%U %Ureason%U
+Syntax: %BCHANKILL%B %U#channel%U <%Ureason%U>
* %BKILL%Bs the users,
* %BKLINE%Bs the users.
-Syntax: OS CLONES [LIST|UINFO|MSG|FJOIN|KILL|KLINE] [msg/reason]
+Syntax: %BCLONES%B <%ULIST|UINFO|MSG|FJOIN|KILL|KLINE%U> [%Umsg/reason%U]
LIST - Lists all users that match.
UINFO - Retrieves UINFO for all users that match. %BWARNING%B May flood you off.
%BOperServ KILL%B KILLs a user off the network.
One possible use is to allow helpers to KILL.
-Syntax: %BKILL%B %Utarget%U %Ureason here%U
+Syntax: %BKILL%B %Utarget%U <%Ureason%U>
Example: KILL erry not kool.
--- /dev/null
+%BOperServ KILLNEW%B gets the list of users that connected within a
+certain period of time.
+
+* retrieves %BUINFO%B,
+* sends a %BMSG%B to the users,
+* %BFJOIN%Bs the users,
+* %BKILL%Bs the users,
+* %BKLINE%Bs the users.
+
+Syntax: %BKILLNEW%B <%ULIST|UINFO|MSG|FJOIN|KILL|KLINE%U> [NOTID] +time [%Umsg/reason%U]
+
+ LIST - Lists all users that match.
+ UINFO - Retrieves UINFO for all users that match. %BWARNING%B May flood you off.
+ MSG - Sends a NOTICE from OperServ to the users. Message is not optional. (DUH)
+ FJOIN - Force-Join to a channel.
+ KILL - Kill the users. Reason is optional, but recommended.
+ KLINE - G:Line the users. Reason is optional, but recommended.
+
+Examples:
+
+ KILLNEW LIST NOTID +5m
+ KILLNEW UINFO +30s
+ KILLNEW KILL NOTID +30s
finished yet). A news item may be permanent (does not expire) or
may have a limited life (expires).
-Syntax: %BLOGONNEWS ADD%B %UU/O%U [%U+expiry%U] %Umessage%U
- %BLOGONNEWS LIST%B %UU/O%U
- %BLOGONNEWS DEL%B %UU/O%U %Unum%U
+Syntax: %BLOGONNEWS ADD%B <%UU|O%U> [%U+expiry%U] <%Umessage%U>
+ %BLOGONNEWS LIST%B <%UU/O%U>
+ %BLOGONNEWS DEL%B <%UU/O%U> <%Unum%U>
%BKILL%B, or %BKLINE%B. if you specify %BNOID%B it will only act on
users that have not identified to any nicks.
-Syntax: OS LONERS [LIST|UINFO|MSG|FJOIN|KILL|KLINE] [NOTID] [msg/reason]
+Syntax: %BLONERS%B [%ULIST|UINFO|MSG|FJOIN|KILL|KLINE%U] [%UNOTID%U] [%Umsg/reason%U]
-If no command is specified, defauls to LIST.
+If no command is specified, defaults to LIST.
LIST - Lists all users in zero channels
UINFO - Retrieves UINFO for all users in zero channels. %BWARNING%B May flood you off.
%BOperServ QLINE%B prevents a nick or nickmask from being used,
except by opers and services agents.
-Syntax: %BQLINE ADD%B [%U+expiry%U] %Umask%U %Ureason%U
- %BQLINE DEL%B %Umask%U
+Syntax: %BQLINE ADD%B [%U+expiry%U] <%Umask%U> <%Ureason%U>
+ %BQLINE DEL%B <%Umask%U>
%BQLINE LIST%B
This command is limited to %BServices Roots%B, as it is
%Uhighly%U abuseable.
-Syntax: %BSVSKILL%B %Utarget%U %Ureason here%U
+Syntax: %BSVSKILL%B <%Utarget%U> <%Ureason here%U>
Example: SVSKILL Alucard Quit: I am the very model of a modern major general.
%BOperServ SVSNICK%B forcibly changes a user's nick.
-Syntax: %BSVSNICK%B %Uoldnick%U %Unewnick%U
+Syntax: %BSVSNICK%B <%Uoldnick%U> <%Unewnick%U>
--- /dev/null
+%BSpamServ%B allows you to watch for unwanted spam.
+
+Commands:
+ WATCH Modify channels being watched
--- /dev/null
+%BSpamServ LISTCONF%B lists the known settings of
+the current configuration.
+
+Syntax: %LISTCONF%B
--- /dev/null
+%BSpamServ REHASH%B reloads the values of
+the configuration. This does not save any
+recently set values with the %BSET%B command.
+
+Syntax: %BREHASH%B
--- /dev/null
+%BSpamServ SAVE%B saves the list of watched channels
+as well as the current configuration.
+
+Syntax: %BSAVE%B
--- /dev/null
+%BSpamServ SET%B allows you to modify the
+configuration on the fly.
+
+Syntax: %BSET%B %Uoption%U <%Uvalue%U>
+
+Caveats: This command is limited toi previously
+known options in the configuration file. Use
+%BSpamServ LISTCONF%B to list those options.
--- /dev/null
+%BSpamServ WATCH%B modifies the list of channels being watched.
+
+ ADD - Add a channel to be watched for spam.
+ DEL - Remove a channel from being watched.
+ LIST - List channels currently being watched.
--- /dev/null
+%SpamServ WATCH ADD%B adds the specified channel
+to be watched by the SpamServ pseudoclients.
+
+Syntax: %BWATCH ADD%B %U#channel%U
--- /dev/null
+%BSpamServ WATCH DEL%B removes a channel from the watch
+list, causing the SpamServ pseudoclient to part the
+channel.
+
+Syntax: %BWATCH DEL%B %U#channel%U
--- /dev/null
+%BSpamServ WATCH LIST%B lists the currently watched
+channels.
+
+Syntax: %BWATCH LIST%B
#!/bin/bash
-for X in `pgrep services.pl`; do
- if [[ `vdir /proc/$X/cwd |sed 's/.*-> //'` == `pwd` ]]; then
- kill $X
- fi
+for X in `cat data/worker.pids`; do
+ kill $X
done
#shift @sargs if($sign == 2);
my $t = shift @sargs;
if($type == MERGE) {
- $status{$x}{lc $t} = $sign;
+ my $key;
+ if($x =~ /^[beIk]$/) {
+ $key = $t;
+ } else {
+ $key = lc $t;
+ }
+ $status{$x}{$key} = $sign;
}
next;
}
#shift @dargs if($sign == 2);
my $t = shift @dargs;
if($type == MERGE) {
- $status{$x}{lc $t} = $sign;
+ my $key;
+ if($x =~ /^[beIk]$/) {
+ $key = $t;
+ } else {
+ $key = lc $t;
+ }
+ $status{$x}{$key} = $sign;
}
next;
}
use strict;
no strict 'refs';
-use SrSv::IRCd::State 'initial_synced';
+use SrSv::IRCd::State qw( initial_synced synced );
use SrSv::IRCd::Event 'addhandler';
+use SrSv::RunLevel qw( $runlevel :levels );
-use SrSv::Conf2Consts qw(main);
+use SrSv::Conf::Parameters connectserv => [
+ [ joinpart => 0 ],
+];
+
+use SrSv::Conf2Consts qw( main connectserv );
use SrSv::Log;
addhandler('NICKCONN', undef, undef, 'connectserv::ev_nickconn', 1);
sub ev_nickconn {
my ($nick, $ident, $host, $server, $gecos) = @_[0,3,4,5,9];
-
+
$userlist{lc $nick} = [$ident, $host, $gecos, $server];
-
+
return unless initial_synced();
message("\00304\002SIGNED ON\002 user: \002$nick\002 ($ident\@$host - $gecos\017\00304) at $server");
}
sub ev_identchange {
my (undef, $nick, $ident) = @_;
- my ($oldident, $host, $gecos, $server) = @{$userlist{lc $nick}} if (defined($userlist{lc $nick}));
+ my ($oldident, $host, $gecos, $server);
+ ($oldident, $host, $gecos, $server) = @{$userlist{lc $nick}} if (defined($userlist{lc $nick}));
$userlist{lc $nick} = [$ident, $host, $gecos, $server];
message("\00310\002IDENT CHANGE\002 user: \002$nick\002 ($oldident\@$host) changed their virtual ident to \002$ident\002");
addhandler('QUIT', undef, undef, 'connectserv::ev_quit', 1);
sub ev_quit {
my ($nick, $reason) = @_;
+ return unless synced() && $runlevel == ST_NORMAL;
my ($ident, $host, $gecos, $server);
if(defined($userlist{lc $nick})) {
($ident, $host, $gecos, $server) = @{$userlist{lc $nick}};
}
}
+addhandler('SJOIN', undef, undef, 'connectserv::chan_join', 1) if connectserv_conf_joinpart;
+sub chan_join {
+ my ($server, $cn, $ts, $chmodes, $chmodeparms, $userarray, $banarray, $exceptarray) = @_;
+ return unless synced() && $runlevel == ST_NORMAL;
+ foreach my $user (@$userarray) {
+ my $nick = $user->{NICK};
+ message ("\00310CHANNEL JOIN: \002$nick\002 joined to \002$cn\002\003");
+ }
+}
+
+addhandler('PART', undef, undef, 'connectserv::chan_part', 1) if connectserv_conf_joinpart;
+sub chan_part {
+ my ($nick, $cn) = @_;
+ return unless synced() && $runlevel == ST_NORMAL;
+ message ("\00310CHANNEL PART: \002$nick\002 parted from \002$cn\002\003");
+}
+
+addhandler('JOIN', undef, undef, 'connectserv::chan_join0', 1) if connectserv_conf_joinpart;
+sub chan_join0 {
+ my ($nick, $cn) = @_;
+ return unless synced() && $runlevel == ST_NORMAL;
+ if($cn eq '0') {
+ message ("\00310CHANNEL PART: \002$nick\002 parted all channels\003");
+ } else {
+ message ("\00310CHANNEL JOIN: \002$nick\002 joined to \002$cn\002\003");
+ }
+}
+
sub message(@) {
ircd::privmsg($csnick, main_conf_diag, @_);
write_log('diag', '<'.$csnick.'>', @_);
addhandler('NOTICE', undef, lc $esnick, 'echoserv::ev_notice');
sub ev_notice { ircd::notice($_[1], $_[0], $_[2]) }
-agent_connect($esnick, 'services', 'services.SC.net', '+pqzBGHS', 'Echo Server');
+agent_connect($esnick, 'services', undef, '+pqzBGHS', 'Echo Server');
agent_join($esnick, main_conf_diag);
ircd::setmode($esnick, main_conf_diag, '+o', $esnick);
#normalize stuff like "London, City of"
$regionName = join(' ', reverse(split(', ', $regionName)));
}
- $location .= (defined($city) && length($city)) ? ' ' : '(';
+ $location .= (defined($city) && length($city)) ? ', ' : '(';
$location .= "$regionName)";
- } else {
+ } elsif(defined($city) && length($city)) {
$location .= ')';
}
if(defined($metro)) {
use SrSv::Time;
use SrSv::Agent;
use SrSv::HostMask qw( parse_hostmask );
-#use SrSv::Conf qw(main sql);
use SrSv::Conf2Consts qw(main sql);
use SrSv::SimpleHash qw(readHash writeHash);
use SrSv::MySQL '$dbh';
use SrSv::MySQL::Glob;
-use SrSv::Shared qw(%conf %torip %unwhois);
+use SrSv::Shared qw(%conf $torip %unwhois);
-use SrSv::Process::InParent qw(list_conf loadconf saveconf);
+use SrSv::Process::InParent qw(list_conf loadconf saveconf update_tor_list);
use SrSv::TOR;
-#use modules::ss2tkl; # package securitybot::ss2tkl, but has exports
-
#this stuff needs to be put into files
our $sbnick = "SecurityBot";
our $ident = 'Security';
};
sub init {
+ return if main::COMPILE_ONLY();
my $tmpdbh = DBI->connect(
"DBI:mysql:".sql_conf_mysql_db,
sql_conf_mysql_user,
}
sub register {
- agent_connect($sbnick, $ident, $vhost, $umodes, $gecos);
+ agent_connect($sbnick, $ident, undef, $umodes, $gecos);
ircd::sqline($sbnick, 'Reserved for Services');
agent_join($sbnick, main_conf_diag);
}
sub start_timers2 {
- update_tor_list_timed(3540) if $conf{'EnableTor'};
+ update_tor_list_timed(3540) if $enabletor;
#securitybot::ss2tkl::update_ss_timed(3300) if $conf{'EnableSS'};
};
sub check_blacklists($$) {
my ($rnick, $ip) = @_;
- if(initial_synced and $enabletor && $torip{$ip}) {
+ if(initial_synced and $enabletor && $torip->{$ip}) {
if (lc $enabletor eq lc 'vhost') {
ircd::chghost($sbnick, $rnick, misc::gen_uuid(1, 20).'.session.tor');
} else {
return 0;
}
- if($conf{'BanCountry'} && module::is_loaded('country') && (my $country = check_country($ip))) {
+sub hasGeoCountry() {
+ return module::is_loaded('country') || module::is_loaded('geoip');
+}
+
+ if($conf{'BanCountry'} && hasGeoCountry() && (my $country = check_country($ip))) {
ircd::zline($sbnick, $ip, $conf{'ProxyZlineTime'}, mk_banreason($conf{'CountryZlineReason'}, $country));
return 0;
}
add_timer('', $time, __PACKAGE__, 'securitybot::update_tor_list_timed');
- update_tor_list();
+ update_tor_list() if $enabletor;
}
sub update_tor_list() {
+ return unless (defined($conf{'TorServer'}) && length($conf{'TorServer'}));
diagmsg( " -- Loading Tor server list.");
# path may be a local one if you run a tor-client.
# most configs are /var/lib/tor/cached-directory
my %newtorip;
- foreach my $torIP (getTorRouters($conf{'TorServer'})) {
+ my @entries;
+ local $SIG{__DIE__} = undef;
+ eval {
+ @entries = getTorRouters($conf{'TorServer'});
+ };
+ if($@) {
+ ircd::debug("SecurityBot failed to load TOR data", $@);
+ return;
+ }
+ foreach my $torIP (@entries) {
$newtorip{$torIP} = 1;
}
my $torcount = scalar(keys(%newtorip));
if($torcount > 0) {
- %torip = %newtorip;
+ $torip = \%newtorip;
diagmsg( " -- Finished loading Tor server list - $torcount servers found.");
} else {
diagmsg( " -- Failed to load Tor server list, CHECK YOUR TorServer SETTING.");
package services;
use strict;
-use SrSv::Conf::Parameters services => [
- [noexpire => undef],
- [nickexpire => 21],
- [vacationexpire => 90],
- [nearexpire => 7],
- [chanexpire => 21],
- [validate_email => undef],
- [validate_expire => 1],
- [clone_limit => 3],
- [chankilltime => 86400],
-
- [default_protect => 'normal'],
- [default_chanbot => undef],
- [old_user_age => 300],
-
- [log_overrides => 0],
-
- [botserv => undef],
- [nickserv => undef],
- [chanserv => undef],
- [memoserv => undef],
- [adminserv => undef],
- [operserv => undef],
- [hostserv => undef],
-];
-
-use DBI;
+use SrSv::Conf::services;
+
use SrSv::MySQL qw($dbh);
use SrSv::Conf qw(main services sql);
use SrSv::Conf2Consts qw(main services sql);
[$adminserv::asnick_default, '+pqzBS', 'Services\' Administration Agent'],
[$hostserv::hsnick_default, '+pqzBS', 'vHost Agent']
);
-if(services_conf_nickserv) {
+if(services_conf_nickserv && (lc(services_conf_nickserv) ne lc($nickserv::nsnick_default)) ) {
push @agents, [services_conf_nickserv, '+opqzBHS', 'Nick Registration Agent'];
$nickserv::nsnick = services_conf_nickserv;
}
-if(services_conf_chanserv) {
+if(services_conf_chanserv && (lc(services_conf_chanserv) ne lc($chanserv::csnick_default)) ) {
push @agents, [services_conf_chanserv, '+pqzBS', 'Channel Registration Agent'];
- $chanserv::csnick = services_conf_nickserv;
+ $chanserv::csnick = services_conf_chanserv;
}
-if(services_conf_operserv) {
+if(services_conf_operserv && (lc(services_conf_operserv) ne lc($operserv::osnick_default)) ) {
push @agents, [services_conf_operserv, '+opqzBHS', 'Operator Services Agent'];
$operserv::osnick = services_conf_operserv;
}
-if(services_conf_memoserv) {
+if(services_conf_memoserv && (lc(services_conf_memoserv) ne lc($memoserv::msnick_default)) ) {
push @agents, [services_conf_memoserv, '+pqzBS', 'Memo Exchange Agent'];
$memoserv::msnick = services_conf_memoserv;
}
-if(services_conf_botserv) {
+if(services_conf_botserv && (lc(services_conf_botserv) ne lc($botserv::bsnick_default)) ) {
push @agents, [services_conf_botserv, '+pqzBS', 'Channel Bot Control Agent'];
$botserv::bsnick = services_conf_botserv;
}
-if(services_conf_adminserv) {
+if(services_conf_adminserv && (lc(services_conf_adminserv) ne lc($adminserv::asnick_default)) ) {
push @agents, [services_conf_adminserv, '+pqzBS', 'Services\' Administration Agent'];
$adminserv::asnick = services_conf_adminserv;
}
-if(services_conf_hostserv) {
+if(services_conf_hostserv && (lc(services_conf_hostserv) ne lc($adminserv::asnick_default)) ) {
push @agents, [services_conf_hostserv, '+pqzBS', 'vHost Agent'];
$hostserv::hsnick = services_conf_hostserv;
}
}
sub init {
+ return if main::COMPILE_ONLY();
my $tmpdbh = DBI->connect("DBI:mysql:".sql_conf_mysql_db, sql_conf_mysql_user, sql_conf_mysql_pass, { AutoCommit => 1, RaiseError => 1 });
$tmpdbh->do("TRUNCATE TABLE chanuser");
S_ROOT => 4,
};
+our (%flags, @levels, @defflags, $allflags);
+
+BEGIN {
# BE CAREFUL CHANGING THESE
my @flags = (
'SERVOP',
'KILL',
'HELP',
);
-my $allflags = (2**@flags) - 1;
-my %flags;
-for(my $i=0; $i<@flags; $i++) {
- $flags{$flags[$i]} = 2**$i;
-}
-
-our $asnick_default = 'AdminServ';
-our $asnick = $asnick_default;
+for(my $i = scalar(@flags) - 1; $i >= 0; $i--) {
+ $flags{$flags[$i]} = 1 << $i;
+}
+$allflags = (1 << scalar(@flags)) - 1;
our @levels = ('Normal User', 'HelpOp', 'Operator', 'Administrator', 'Root');
-my @defflags = (
+# BE CAREFUL CHANGING THESE
+our @defflags = (
0, # Unused
$flags{HELP}, # HelpOp
$flags{HELP}|$flags{FJOIN}|$flags{QLINE}|$flags{SUPER}|$flags{FREEZE}|$flags{KILL}, # Operator
- $flags{HELP}|$flags{FJOIN}|$flags{QLINE}|$flags{SUPER}|$flags{FREEZE}|$flags{HOLD}|$flags{BOT}|$flags{SERVOP}|$flags{KILL}, # Admin
+ $flags{HELP}|$flags{FJOIN}|$flags{QLINE}|$flags{SUPER}|$flags{FREEZE}|$flags{KILL}|
+ $flags{HOLD}|$flags{BOT}|$flags{SERVOP}, # Admin
$allflags # Root
);
+}
+our $asnick_default = 'AdminServ';
+our $asnick = $asnick_default;
+
+
our (
$create_svsop, $delete_svsop, $rename_svsop,
my @args = split(/\s+/, $msg);
my $cmd = shift @args;
- my $user = { NICK => $src, AGENT => $bsnick };
+ my $user = { NICK => $src, AGENT => $dst };
return if flood_check($user);
my $user = { NICK => $src, AGENT => agent($chan) };
my %cmdhash = (
- 'voice' => \&give_ops,
- 'devoice' => \&give_ops,
- 'hop' => \&give_ops,
- 'halfop' => \&give_ops,
- 'dehop' => \&give_ops,
- 'dehalfop' => \&give_ops,
- 'op' => \&give_ops,
- 'deop' => \&give_ops,
- 'protect' => \&give_ops,
+ 'voice' => \&give_ops,
+ 'devoice' => \&give_ops,
+ 'hop' => \&give_ops,
+ 'halfop' => \&give_ops,
+ 'dehop' => \&give_ops,
+ 'dehalfop' => \&give_ops,
+ 'op' => \&give_ops,
+ 'deop' => \&give_ops,
+ 'protect' => \&give_ops,
'admin' => \&give_ops,
- 'deprotect' => \&give_ops,
+ 'deprotect' => \&give_ops,
'deadmin' => \&give_ops,
'up' => \&up,
'commands' => \&help,
'botcmds' => \&help,
+ 'abbrevs' => \&help,
+ 'abbreviations' => \&help,
+ 'abbrev' => \&help,
+
'users' => \&alist,
'alist' => \&alist,
'm' => \&mode,
'resync' => \&resync,
+
+ 'topic' => \&topic,
+ 't' => \&topic,
+
+ 'why' => \&why,
+ 'tempban' => \&tempban,
+ 'tmpban' => \&tempban,
+ "tb" => \&tempban,
);
sub give_ops {
- my ($user, $cmd, $chan, @args) = @_;
+ my ($user, $chan, $cmd, undef, @args) = @_;
chanserv::cs_setmodes($user, $cmd, $chan, @args);
}
sub up {
- my ($user, $cmd, $chan, @args) = @_;
+ my ($user, $chan, $cmd, undef, @args) = @_;
chanserv::cs_updown($user, $cmd, $chan->{CHAN}, @args);
}
sub down {
- my ($user, $cmd, $chan, @args) = @_;
+ my ($user, $chan, $cmd, undef, @args) = @_;
if(lc $cmd eq 'molest') {
chanserv::unset_modes($user, $chan);
} else {
}
sub invite {
- my ($user, $cmd, $chan, @args) = @_;
+ my ($user, $chan, $cmd, undef, @args) = @_;
chanserv::cs_invite($user, $chan, @args) unless @args == 0;
}
sub kick {
- my ($user, $cmd, $chan, @args) = @_;
- my $target = shift @args;
+ my ($user, $chan, $cmd, undef, @args) = @_;
+ my $target = shift @args or return;
chanserv::cs_kick($user, $chan, $target, 0, join(' ', @args));
}
+ sub tempban {
+ my ($user, $chan, $cmd, undef, @args) = @_;
+
+ my $cn = $chan->{CHAN};
+ use Data::Dumper;
+
+ unshift @args, $cn;
+ print ("ARGS " . Dumper (@args));
+ chanserv::cs_tempban($user, join(' ', @args));
+ }
sub kickban {
- my ($user, $cmd, $chan, @args) = @_;
- my $target = shift @args;
+ my ($user, $chan, $cmd, undef, @args) = @_;
+ my $target = shift @args or return;
chanserv::cs_kick($user, $chan, $target, 1, join(' ', @args));
}
sub kickmask {
- my ($user, $cmd, $chan, @args) = @_;
- my $target = shift @args;
+ my ($user, $chan, $cmd, undef, @args) = @_;
+ my $target = shift @args or return;
chanserv::cs_kickmask($user, $chan, $target, 0, join(' ', @args));
}
sub kickbanmask {
- my ($user, $cmd, $chan, @args) = @_;
- my $target = shift @args;
+ my ($user, $chan, $cmd, undef, @args) = @_;
+ my $target = shift @args or return;
chanserv::cs_kickmask($user, $chan, $target, 1, join(' ', @args));
}
sub calc {
- my ($user, $cmd, $chan, @args) = @_;
+ my ($user, $chan, $cmd, undef, @args) = @_;
my $msg = join(' ', @args);
for ($msg) {
s/,/./g;
}
sub seen {
- my ($user, $cmd, $chan, @args) = @_;
+ my ($user, $chan, $cmd, undef, @args) = @_;
if(@args >= 1) {
nickserv::ns_seen($user, @args);
}
sub help {
- my ($user, $cmd, $chan, @args) = @_;
- sendhelp($user, 'chanbot');
+ my ($user, $chan, $cmd, undef, @args) = @_;
+ if($cmd =~ /^abbrev(iation)?s?$/) {
+ sendhelp($user, 'chanbot', 'abbreviations');
+ } else {
+ sendhelp($user, 'chanbot');
+ }
}
sub alist {
- my ($user, $cmd, $chan, @args) = @_;
+ my ($user, $chan, $cmd, undef, @args) = @_;
chanserv::cs_alist($user, $chan);
}
sub unban {
- my ($user, $cmd, $chan, @args) = @_;
+ my ($user, $chan, $cmd, undef, @args) = @_;
if(@args == 0) {
chanserv::cs_unban($user, $chan, get_user_nick($user));
}
}
sub ban {
- my ($user, $cmd, $chan, @args) = @_;
+ my ($user, $chan, $cmd, undef, @args) = @_;
$cmd =~ /^(q|n)?ban$/; my $type = $1;
if(@args >= 1) {
chanserv::cs_ban($user, $chan, $type, @args);
}
sub banlist {
- my ($user, $cmd, $chan, @args) = @_;
+ my ($user, $chan, $cmd, undef, @args) = @_;
chanserv::cs_banlist($user, $chan);
}
sub dice {
# FIXME: If dice is disabled, don't count towards flooding.
- my ($user, $cmd, $chan, @args) = @_;
-
+ my ($user, $chan, $cmd, undef, @args) = @_;
+
if(chanserv::can_do($chan, 'DICE', $user)) {
ircd::privmsg(agent($chan), $chan->{CHAN},
get_dice($args[0]));
}
sub mode {
- my ($user, $cmd, $chan, @args) = @_;
+ my ($user, $chan, $cmd, undef, @args) = @_;
if(@args >= 1) {
chanserv::cs_mode($user, $chan, shift @args, @args);
}
}
sub resync {
- my ($user, $cmd, $chan) = @_;
+ my ($user, $chan, $cmd) = @_;
chanserv::cs_resync($user, $chan->{CHAN});
}
+ sub topic {
+ my ($user, $chan, $cmd, $msg) = @_;
+ if (@args >= 1) {
+ $msg =~ s/^!$cmd //;
+ chanserv::cs_topic($user, $chan, $msg);
+ }
+ }
+
+ sub why {
+ my ($user, $chan, $cmd, undef, @args) = @_;
+
+ if(@args >= 1) {
+ chanserv::cs_why($user, $chan, @args);
+ } else {
+ notice($user, 'Syntax: WHY <nick> [nick ...]');
+ }
+ }
if(defined($cmdhash{$cmd})) {
return if flood_check($user);
- &{$cmdhash{$cmd}}($user, $cmd, $chan, @args);
+ &{$cmdhash{$cmd}}($user, $chan, $cmd, $msg, @args);
}
}
if cr_chk_flag($chan, CRF_VERBOSE());
ircd::privmsg(agent($chan), $cn, $botmsg);
} else {
- notice($user, $err_deny);
+ # can_do will give the $err_deny for us.
+ #notice($user, $err_deny);
}
}
package chanserv;
use strict;
-use DBI qw(:sql_types);
use SrSv::Timer qw(add_timer);
-
+use Data::Dumper;
use SrSv::Message qw(current_message);
use SrSv::IRCd::State qw($ircline synced initial_synced %IRCd_capabilities);
use SrSv::Message qw(message current_message);
use SrSv::HostMask qw(normalize_hostmask make_hostmask parse_mask);
-
#FIXME: This needs to be abstracted into a proper SrSv::IRCd module
use SrSv::Unreal::Modes qw(@opmodes %opmodes $scm $ocm $acm sanitize_mlockable);
use SrSv::IRCd::Validate qw( valid_nick validate_chmodes validate_ban );
use SrSv::Shared qw(%enforcers $chanuser_table);
#use SrSv::Conf qw(services);
-use SrSv::Conf2Consts qw( services sql );
+use SrSv::Conf2Consts qw( services sql main );
use SrSv::Time;
use SrSv::Text::Format qw( columnar enum );
use SrSv::Log;
-use SrSv::User qw(get_user_nick get_user_id is_online :user_flags get_host get_vhost :flags :flood);
+use SrSv::User qw(
+ get_user_nick get_user_agent get_user_id
+ is_online :user_flags get_host get_vhost
+ :flags :flood
+ );
use SrSv::User::Notice;
use SrSv::Help qw( sendhelp );
use SrSv::ChanReg::Flags;
use SrSv::NickReg::Flags;
+use SrSv::NickReg::NickText;
use SrSv::NickReg::User qw(is_identified get_nick_users get_nick_user_nicks);
-use SrSv::MySQL '$dbh';
+use SrSv::MySQL qw( $dbh :sql_types );
use SrSv::MySQL::Glob;
use SrSv::Util qw( makeSeqList );
AKICK => 1,
LEVELS => 1,
COPY => 1,
+ WELCOME => 1,
}
],
['SUPER',
{
BAN => 1,
+ UNBANSELF => 1,
+ UNBAN => 1,
KICK => 1,
VOICE => 1,
HALFOP => 1,
SETTOPIC => 1,
INVITE => 1,
INVITESELF => 1,
- JOIN => 1,
CLEAR => 1,
AKICKENFORCE => 1,
UPDOWN => 1,
],
['HELP',
{
+ JOIN => 1,
ACCLIST => 1,
LEVELSLIST => 1,
AKICKLIST => 1,
our (
$get_joinpart_lock, $get_modelock_lock, $get_update_modes_lock,
- $chanjoin, $chanpart, $chop, $chdeop, $get_op, $get_user_chans, $get_user_chans_recent,
+ $chanjoin, $chanpart, $chanpart2, $chop, $chdeop, $get_op, $get_user_chans, $get_user_chans_recent,
$get_all_closed_chans, $get_user_count,
$is_in_chan,
$set_bantype, $get_bantype,
$drop_chantext, $drop_nicktext,
-
- $get_recent_private_chans,
+ $get_host,
+ $get_host_inchan,
+ $get_expired_bans,
);
sub init() {
$chanjoin = $dbh->prepare("REPLACE INTO chanuser (seq,nickid,chan,op,joined) VALUES (?, ?, ?, ?, 1)");
$chanpart = $dbh->prepare("UPDATE chanuser SET joined=0, seq=?
WHERE nickid=? AND chan=? AND (seq <= ? OR seq > ?)");
+ $chanpart2 = $dbh->prepare("UPDATE chanuser SET joined=0 WHERE nickid=? AND chan=?");
#$chop = $dbh->prepare("UPDATE chanuser SET op=op+? WHERE nickid=? AND chan=?");
$chop = $dbh->prepare("UPDATE chanuser SET op=IF(op & ?, op, op ^ ?) WHERE nickid=? AND chan=?");
$chdeop = $dbh->prepare("UPDATE chanuser SET op=IF(op & ?, op ^ ?, op) WHERE nickid=? AND chan=?");
$get_info = $dbh->prepare("SELECT chanreg.descrip, chanreg.regd, chanreg.last, chantext.data,
- chanreg.topicer, chanreg.modelock, foundernick.nick, successornick.nick, chanreg.bot, chanreg.bantype
+ chanreg.topicer, chanreg.modelock, foundernick.nick, successornick.nick, chanreg.bot, chanreg.bantype, chanreg.bantime
FROM nickreg AS foundernick, chanreg
LEFT JOIN nickreg AS successornick ON(successornick.id=chanreg.successorid)
LEFT JOIN chantext ON (chanreg.chan=chantext.chan AND chantext.type=".CRT_TOPIC().")
$register = $dbh->prepare("INSERT INTO chanreg
SELECT ?, ?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), NULL, NULL,
- NULL, id, NULL, NULL, NULL, ".DEFAULT_BANTYPE()." FROM nickreg WHERE nick=?");
+ NULL, id, NULL, NULL, NULL, ".DEFAULT_BANTYPE().",0 FROM nickreg WHERE nick=?");
$register->{PrintError} = 0;
$copy_chanreg = $dbh->prepare("INSERT INTO chanreg
- ( chan, descrip, regd, last, modelock, founderid, successorid, bot, flags, bantype)
- SELECT ?, descrip, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), modelock, founderid, successorid, bot, flags, bantype
+ ( chan, descrip, regd, last, modelock, founderid, successorid, bot, flags, bantype,bantime)
+ SELECT ?, descrip, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), modelock, founderid, successorid, bot, flags, bantype,bantime
FROM chanreg WHERE chan=?");
-
$drop_acc = $dbh->prepare("DELETE FROM chanacc WHERE chan=?");
$drop_lvl = $dbh->prepare("DELETE FROM chanlvl WHERE chan=?");
$drop_akick = $dbh->prepare("DELETE FROM akick WHERE chan=?");
$drop_welcome = $dbh->prepare("DELETE FROM welcome WHERE chan=?");
$count_welcome = $dbh->prepare("SELECT COUNT(*) FROM welcome WHERE chan=?");
$consolidate_welcome = $dbh->prepare("UPDATE welcome SET id=id-1 WHERE chan=? AND id>?");
-
- $add_ban = $dbh->prepare("REPLACE INTO chanban SET chan=?, mask=?, setter=?, type=?, time=UNIX_TIMESTAMP()");
+ $add_ban = $dbh->prepare("INSERT IGNORE INTO chanban SET chan=?, mask=?, setter=?, type=?, time=UNIX_TIMESTAMP()");
$delete_bans = $dbh->prepare("DELETE FROM chanban WHERE chan=? AND ? LIKE mask AND type=?");
# likely need a better name for this or for the above.
$delete_ban = $dbh->prepare("DELETE FROM chanban WHERE chan=? AND mask=? AND type=?");
$find_bans = $dbh->prepare("SELECT mask FROM chanban WHERE chan=? AND ? LIKE mask AND type=?");
$get_all_bans = $dbh->prepare("SELECT mask FROM chanban WHERE chan=? AND type=?");
- $get_ban_num = $dbh->prepare("SELECT mask FROM chanban WHERE chan=? ORDER BY time, mask LIMIT 1 OFFSET ?");
- $get_ban_num->bind_param(2, 0, SQL_INTEGER);
+ $get_ban_num = $dbh->prepare("SELECT mask FROM chanban WHERE chan=? AND type=? ORDER BY time, mask LIMIT 1 OFFSET ?");
+ $get_ban_num->bind_param(3, 0, SQL_INTEGER);
$list_bans = $dbh->prepare("SELECT mask, setter, time FROM chanban WHERE chan=? AND type=? ORDER BY time, mask");
$wipe_bans = $dbh->prepare("DELETE FROM chanban WHERE chan=?");
WHERE chan=? AND user.id=? AND type=? AND $chanban_mask");
$add_auth = $dbh->prepare("REPLACE INTO nicktext
- SELECT nickalias.nrid, (".nickserv::NTF_AUTH()."), 1, ?, ? FROM nickalias WHERE nickalias.alias=?");
+ SELECT nickalias.nrid, (".NTF_AUTH()."), 1, ?, ? FROM nickalias WHERE nickalias.alias=?");
$list_auth_chan = $dbh->prepare("SELECT nickreg.nick, nicktext.data FROM nickreg, nicktext
- WHERE nickreg.id=nicktext.nrid AND nicktext.type=(".nickserv::NTF_AUTH().") AND nicktext.chan=?");
+ WHERE nickreg.id=nicktext.nrid AND nicktext.type=(".NTF_AUTH().") AND nicktext.chan=?");
$get_auth_nick = $dbh->prepare("SELECT nicktext.data FROM nickreg, nickalias, nicktext
- WHERE nickreg.id=nicktext.nrid AND nickreg.id=nickalias.nrid AND nicktext.type=(".nickserv::NTF_AUTH().")
+ WHERE nickreg.id=nicktext.nrid AND nickreg.id=nickalias.nrid AND nicktext.type=(".NTF_AUTH().")
AND nicktext.chan=? AND nickalias.alias=?");
$get_auth_num = $dbh->prepare("SELECT nickreg.nick, nicktext.data FROM nickreg, nickalias, nicktext
- WHERE nickreg.id=nicktext.nrid AND nickreg.id=nickalias.nrid AND nicktext.type=(".nickserv::NTF_AUTH().")
+ WHERE nickreg.id=nicktext.nrid AND nickreg.id=nickalias.nrid AND nicktext.type=(".NTF_AUTH().")
AND nicktext.chan=? LIMIT 1 OFFSET ?");
$get_auth_num->bind_param(2, 0, SQL_INTEGER);
$find_auth = $dbh->prepare("SELECT 1 FROM nickalias, nicktext
- WHERE nickalias.nrid=nicktext.nrid AND nicktext.type=(".nickserv::NTF_AUTH().")
+ WHERE nickalias.nrid=nicktext.nrid AND nicktext.type=(".NTF_AUTH().")
AND nicktext.chan=? AND nickalias.alias=?");
$set_bantype = $dbh->prepare("UPDATE chanreg SET bantype=? WHERE chan=?");
$drop_chantext = $dbh->prepare("DELETE FROM chantext WHERE chan=?");
$drop_nicktext = $dbh->prepare("DELETE nicktext.* FROM nicktext WHERE nicktext.chan=?");
+ $get_host = $dbh->prepare ("SELECT user.host from user where user.nick=?");
+ $get_host_inchan = $dbh->prepare ("SELECT clonedUsers.nick FROM user AS curUser
+ JOIN user AS clonedUsers ON (curUser.host=clonedUsers.host)
+ JOIN chanuser ON (chanuser.nickid=clonedUsers.id)
+ WHERE clonedUsers.id!=curUser.id AND curUser.id=? AND chanuser.chan=? AND chanuser.joined=1");
+ $get_expired_bans = $dbh->prepare("SELECT channel, banmask, expiry, timeset FROM tmpban
+ WHERE expiry < UNIX_TIMESTAMP()");
- $get_recent_private_chans = $dbh->prepare("SELECT chanuser.chan FROM chanperm, chanlvl, chanuser, nickid, chanacc WHERE chanperm.name='Join' AND chanlvl.perm=chanperm.id AND chanlvl.level > 0 AND nickid.id=? AND chanacc.nrid=nickid.nrid AND chanuser.nickid=nickid.id AND chanuser.joined=0 AND chanuser.chan=chanacc.chan AND chanlvl.level <= chanacc.level");
}
use SrSv::MySQL::Stub {
set_lastop => ['NULL', "UPDATE chanreg SET last=UNIX_TIMESTAMP() WHERE chan=?"],
set_lastused => ['NULL', "UPDATE chanacc, nickid SET chanacc.last=UNIX_TIMESTAMP() WHERE
chanacc.chan=? AND nickid.id=? AND chanacc.nrid=nickid.nrid AND chanacc.level > 0"],
+ get_recent_private_chans => ['COLUMN', "SELECT DISTINCT chanuser.chan FROM chanuser
+ JOIN chanacc ON (chanuser.chan=chanacc.chan AND chanuser.joined=0)
+ JOIN chanlvl ON (chanlvl.level <= chanacc.level AND chanlvl.level > 0 AND chanuser.chan=chanlvl.chan)
+ JOIN chanperm ON (chanlvl.perm=chanperm.id)
+ JOIN nickid ON (chanuser.nickid=nickid.id AND chanacc.nrid=nickid.nrid)
+ WHERE chanperm.name='Join'
+ AND nickid.id=?"],
+ add_tempban => ['INSERT', "INSERT INTO tmpban values (?,?,UNIX_TIMESTAMP()+?,UNIX_TIMESTAMP())"],
+ del_tempban => ['NULL', "DELETE FROM tmpban WHERE channel=? AND banmask = ?"],
+ __get_bantime => ['SCALAR', "SELECT bantime FROM chanreg WHERE chan=?"],
+ set_bantime => ['NULL', "UPDATE chanreg SET bantime=? WHERE chan=?"],
};
+
+sub get_bantime($) {
+ my ($chan) = @_;
+ my $cn;
+ if(ref $chan) {
+ if(exists $chan->{BANTIME}) {
+ return $chan->{BANTIME};
+ }
+ $cn = $chan->{CHAN};
+ } else {
+ $cn = $chan;
+ }
+ my $bantime = __get_bantime($cn);
+ if(ref $chan) {
+ $chan->{BANTIME} = $bantime;
+ }
+ return $bantime;
+}
+
### CHANSERV COMMANDS ###
+our %high_priority_cmds = (
+ kick => 1,
+ mode => 1,
+ kb => 1,
+ kickban => 1,
+ kickb => 1,
+ kban => 1,
+ down => 1,
+);
+sub check_expired_bans() {
+ add_timer('ChanServ Expire', 10, __PACKAGE__, 'chanserv::check_expired_bans');
+ $get_expired_bans->execute();
+
+ while (my ($cn, $ban) = $get_expired_bans->fetchrow_array()) {
+ my $chan = { CHAN => $cn };
+ ircd::setmode(agent($chan), $cn, '-b', $ban);
+ }
+ ircd::flushmodes();
+}
+sub tempban($@) {
+ my ($chan, @bans) = @_;
+
+ my $cn = $chan->{CHAN};
+ return unless scalar(@bans);
+
+ foreach my $ban (@bans) {
+ my ($mask, $expiry);
+ if(ref($ban)) {
+ ($mask, $expiry) = @$ban;
+ } else {
+ $mask = $ban;
+ }
+ if($expiry) {
+ add_tempban($cn, $mask, $expiry);
+ }
+ }
+
+ ircd::ban_list(agent($chan), $cn, +1, 'b', map { ref($_) ? $_->[0] : $_ } @bans);
+}
+sub clones_exist ($$) {
+ my ($user, $chan) = @_;
+ my $cn = $chan->{CHAN};
+
+ unless(cr_chk_flag($chan, CRF_NOCLONES)) {
+ return;
+ }
+
+ my $nick = $user->{NICK};
+ $get_host_inchan->execute(get_user_id($user), $cn);
+ my ($joined) = $get_host_inchan->fetchrow_array;
+ $get_host_inchan->finish();
+
+ if ($joined) {
+ return $joined;
+ }
+
+ return 0;
+}
sub dispatch($$$) {
my ($src, $dst, $msg) = @_;
-
+
$msg =~ s/^\s+//;
my @args = split(/\s+/, $msg);
my $cmd = shift @args;
return if flood_check($user);
+ if(!defined($high_priority_cmds{lc $cmd}) &&
+ !adminserv::is_svsop($user) &&
+ $SrSv::IRCd::State::queue_depth > main_conf_queue_highwater)
+ {
+ notice($user, get_user_agent($user)." is too busy right now. Please try your command again later.");
+ return;
+ }
+
if($cmd =~ /^register$/i) {
if(@args >= 1) {
my @args = split(/\s+/, $msg, 4);
notice($user, 'Syntax: REGISTER <#channel> [password] [description]');
}
}
+ elsif ($cmd =~ /^t((e)?mp)?b(an)?$/i) {
+ my @args = split (/\s+/, $msg, 2);
+
+ cs_tempban ($user, $args[1]);
+ }
elsif($cmd =~ /^(?:[uvhas]op|co?f(ounder)?)$/i) {
my ($cn, $cmd2) = splice(@args, 0, 2);
my $chan = { CHAN => $cn };
-
+
if($cmd2 =~ /^add$/i) {
if(@args == 1) {
cs_xop_add($user, $chan, $cmd, $args[0]);
notice($user, 'Syntax: COUNT <channel>');
}
}
- elsif($cmd =~ /^kick$/i) {
+ elsif($cmd =~ /^k(?:ick)?$/i) {
my @args = split(/\s+/, $msg, 4); shift @args;
if(@args >= 2) {
cs_kick($user, { CHAN => $args[0] }, $args[1], 0, $args[2])
cs_mlock($user, { CHAN => $chan }, @args)
}
else {
- notice($user, 'Syntax: MLOCK <channel> <ADD|DEL> <modes> [parms]');
+ notice($user, 'Syntax: MLOCK <channel> <ADD|DEL|SET|RESET> <modes> [parms]');
}
}
elsif($cmd =~ /^resync$/i) {
cs_resync($user, @args);
}
}
+ elsif($cmd =~ /^JOIN$/i) {
+ if (@args == 0) {
+ notice($user, 'Syntax: JOIN <chan1> [chan2 [chan3 [..]]]');
+ } else {
+ cs_join($user, @args);
+ }
+ }
+ elsif($cmd =~ /^topic$/i) {
+ my $chan = shift @args;
+ if (@args == 0) {
+ notice($user, 'Syntax: TOPIC <#channel> <message|NONE>');
+ } else {
+ $msg =~ s/^topic #(?:\S+)? //i;
+ cs_topic($user, { CHAN => $chan }, $msg);
+ }
+ }
+ elsif($cmd =~ /^topicappend$/i) {
+ my $chan = shift @args;
+ if (@args == 0) {
+ notice($user, 'Syntax: TOPICAPPEND <#channel> <message>');
+ } else {
+ $msg =~ s/^topicappend #(?:\S+)? //i;
+ cs_topicappend($user, $chan, $msg);
+ }
+ }
+ elsif($cmd =~ /^topicprepend$/i) {
+ my $chan = shift @args;
+ if (@args == 0) {
+ notice($user, 'Syntax: TOPICPREPEND <#channel> <message>');
+ } else {
+ $msg =~ s/^topicprepend #(?:\S+)? //i;
+ cs_topicprepend($user, $chan, $msg);
+ }
+ }
else {
notice($user, "Unrecognized command \002$cmd\002.", "For help, type: \002/msg chanserv help\002");
wlog($csnick, LOG_DEBUG(), "$src tried to use $csnick $msg");
return;
}
+ if(services_conf_chanreg_needs_oper && !adminserv::is_svsop($user)) {
+ notice($user, "You must be network staff to register a channel\n");
+ return;
+ }
unless(get_op($user, $chan) & ($opmodes{o} | $opmodes{a} | $opmodes{q})) {
# This would be preferred to be a 'opmode_mask' or something
# However that might be misleading due to hop not being enough to register
- notice($user, "You must have channel operator status to register \002$cn\002.");
+ notice($user, "You must have channel operator status to register \002$cn\002.");
return;
}
notice($user, ' ', "\002NOTICE:\002 Channel passwords are not used, as a security precaution.")
if $pass;
set_acc($root, $user, $chan, FOUNDER);
- $set_modelock->execute('+rnt', $cn);
+ $set_modelock->execute(services_conf_default_channel_mlock, $cn);
do_modelock($chan);
services::ulog($csnick, LOG_INFO(), "registered $cn", $user, $chan);
botserv::bs_assign($user, $chan, services_conf_default_chanbot) if services_conf_default_chanbot;
}
my $root = get_root_nick($nick);
+ my $srcnick = can_do($chan, 'ACCLIST', $user);
del_acc($root, $chan);
ircd::notice(agent($chan), '%'.$cn, "\002$src\002 $log_str")
if cr_chk_flag($chan, CRF_VERBOSE);
services::ulog($csnick, LOG_INFO(), $log_str, $user, $chan);
- my $srcnick = can_do($chan, 'ACCLIST', $user);
memolog($chan, "\002$srcnick\002 $log_str");
}
memolog($chan, "\002$adder\002 $log_str");
}
-sub cs_akick_enforce($$$) {
+sub cs_akick_enforce($$) {
my ($user, $chan) = @_;
my $cn = $chan->{CHAN};
next;
}
- my ($descrip, $regd, $last, $topic, $topicer, $modelock, $founder, $successor, $bot, $bantype) = @result;
+ my ($descrip, $regd, $last, $topic, $topicer, $modelock, $founder, $successor, $bot, $bantype,$bantime) = @result;
$modelock = modes::sanitize($modelock) unless can_do($chan, 'GETKEY', $user, { NOREPLY => 1 });
push @opts, 'Verbose' if cr_chk_flag($chan, CRF_VERBOSE);
push @opts, 'NeverOp' if cr_chk_flag($chan, CRF_NEVEROP);
push @opts, 'Ban type '.$bantype if $bantype;
+ push @opts, 'Ban time '.$bantime . ' seconds' if $bantype;
my $opts = join(', ', @opts);
my @data;
'autovoice' => 1, 'avoice' => 1,
'neverop' => 1, 'noop' => 1,
+ 'noclones' => 1,
+ 'bantime' => 1,
);
my %override_set = (
'hold' => 'SERVOP', 'noexpire' => 'SERVOP', 'no-expire' => 'SERVOP',
- 'freeze' => 'FREEZE', 'botstay' => 'BOT',
+ 'freeze' => 'FREEZE', 'botstay' => 'BOT', 'log' => 'LOG',
);
chk_registered($user, $chan) or return 0;
return;
}
-
+ if($set =~ /^bantime$/i) {
+ if ( ( my $p = substr($parm, 0, 1) ) != '+' ) {
+ $parm = '+' . $parm;
+ }
+ my $time = $parm;
+ unless ($time == 0) {
+ $time = parse_time ($parm);
+ if(!$time) {
+ notice ($user, "Invalid bantime. See /msg chanserv help set bantime for examples.");
+ return;
+ }
+ }
+ set_bantime($time, $cn);
+ notice($user, "Ban time for \002$cn\002 now set to \002$time\002 seconds.");
+ return;
+ }
my $val;
if($parm =~ /^(?:no|off|false|0)$/i) { $val = 0; }
elsif($parm =~ /^(?:yes|on|true|1)$/i) { $val = 1; }
notice($user, "Please say \002on\002 or \002off\002.");
return;
}
-
+ if ($set =~ /^(?:noclones)$/i) {
+ cr_set_flag($chan, CRF_NOCLONES, $val);
+ if($val) {
+ notice($user,
+ "Noclones is now \002ON\002.",
+ "Clones will be kicked out of \002$cn\002."
+ );
+ } else {
+ notice($user,
+ "Noclones is now \002OFF\002.",
+ "People are allowed to bring clones in \002$cn\002."
+ );
+ }
+ }
if($set =~ /^(?:opguard|secureops)$/i) {
cr_set_flag($chan, CRF_OPGUARD, $val);
chk_registered($user, $chan) or return;
- my ($candoNick, $override) = can_do($chan, 'ACCLIST', undef, $user, undef);
+ my $cn = $chan->{CHAN};
+
+ my ($candoNick, $override) = can_do($chan, 'ACCLIST', $user, { OVERRIDE_MSG => "WHY $cn @tnicks" });
return unless $candoNick;
- my $cn = $chan->{CHAN};
my @reply;
foreach my $tnick (@tnicks) {
my $tuser = { NICK => $tnick };
unless(get_user_id($tuser)) {
- notice($user, "\002$tnick\002: No such user.");
- return;
+ push @reply, "\002$tnick\002: No such user.";
+ next;
}
my $has;
unless($n) {
push @reply, "\002$tnick\002 has no access to \002$cn\002.";
}
- if(services_conf_log_overrides && $override) {
- my $src = get_user_nick($user);
- wlog($csnick, LOG_INFO(), "\002$src\002 used override CS WHY $cn $tnick");
- }
}
notice($user, @reply);
}
notice($user, "\002$target\002 is not in \002$cn\002.");
next;
}
-
+
my $top = get_op($tuser, $chan);
if($de) {
notice($user, "\002$target\002 has no $cmd in \002$cn\002.");
next;
}
-
+
if(!$override and get_best_acc($tuser, $chan) > $acc) {
unless($check_override) {
$override = adminserv::can_do($user, 'SUPER');
botserv::bot_part_if_needed(undef(), $chan, "Channel dropped.");
}
+#my ($bansref, $unbansref, $expires) = parse_bans ($user, $chan, '', @targets, 1, $default_expiry);
+#my ($bansref, $unbansref, $expires) = parse_bans ($user, $chan, '', @targets, 1, $expiry);
+sub parse_bans($$$$;$$) {
+ my ($user, $chan, $type, $targetsref, $temp, $expiry) = (@_);
+ my (@bans, @unbans);
+ my ($nick, $override);
+
+ my @targets = @$targetsref;
+ my $cn = $chan->{CHAN};
+ my $src = get_user_nick($user);
+ my $srclevel = get_best_acc($user, $chan);
+
+ ($nick, $override) = can_do($chan, 'BAN', $user, { ACC => $srclevel });
+ return unless $nick;
+
+ my @errors = (
+ ["I'm sorry, $src, I'm afraid I can't do that."],
+ ["They are not in \002$cn\002."],
+ [$err_deny],
+ ["User not found"],
+ );
+
+ foreach my $target (@targets) {
+ my $tuser;
+
+ if ($target =~ /^\+/ && $temp) {
+ $expiry = $target;
+ next;
+ }
+ elsif(ref($target)) {
+ $tuser = $target;
+ }
+ elsif($target =~ /\,/) {
+ push @targets, split(',', $target);
+ next;
+ }
+ elsif($target eq '') {
+ # Should never happen
+ # but it could, given the split above
+ next;
+ }
+ elsif($target =~ /^-/) {
+ $target =~ s/^-//;
+ push @unbans, $target;
+ next;
+ }
+ elsif($target =~ /[!@]+/) {
+ $target = normalize_hostmask($target);
+ if ($temp) {
+ push @bans, [$target, $expiry];
+ } else {
+ push @bans, $target;
+ }
+ next;
+ }
+ elsif(valid_nick($target)) {
+ $tuser = { NICK => $target };
+ }
+ elsif($target = validate_ban($target)) {
+ if ($temp) {
+ push @bans, [$target, $expiry];
+ } else {
+ push @bans, $target;
+ }
+ next;
+ } else {
+ notice($user, "Not a valid ban target: $target");
+ next;
+ }
+
+ my $targetlevel = get_best_acc($tuser, $chan);
+
+ if(lc $target eq lc agent($chan) or adminserv::is_service($tuser)) {
+ push @{$errors[0]}, get_user_nick($tuser);
+ next;
+ }
+
+ unless(get_user_id($tuser)) {
+ push @{$errors[1]}, get_user_nick($tuser);
+ next;
+ }
+
+ if( $srclevel <= $targetlevel and not ($override && check_override($user, 'BAN', "BAN $cn $target")) ) {
+ push @{$errors[2]}, $target;
+ next;
+ }
+
+ if ($temp) {
+ push @bans, [make_banmask($chan, $tuser, $type), $expiry];
+ } else {
+ push @bans, make_banmask($chan, $tuser, $type);
+ }
+ }
+
+ if (!is_registered($chan)) {
+ notice ($user,
+ "$cn is not registered"
+ );
+ return;
+ }
+
+ foreach my $errlist (@errors) {
+ if(@$errlist > 1) {
+ my $msg = shift @$errlist;
+
+ foreach my $e (@$errlist) { $e = "\002$e\002" }
+
+ notice($user,
+ "Cannot ban ".
+ enum("or", @$errlist).
+ ": $msg"
+ );
+ }
+ }
+
+ return (\@bans, \@unbans);
+}
+
+sub cs_tempban($$) {
+ my ($user, $argstring) = @_;
+ my ( $expiry, $cn, $chan );
+
+ my @args = split(/ /, $argstring);
+ my $numargs = scalar @args;
+
+ for (my $i = 0; $i < $numargs; $i++) {
+ if ($args[$i] =~ /\#/) {
+ $cn = $args[$i];
+ $chan = { CHAN => $cn };
+ splice (@args, $i, 1);
+ }
+ }
+
+ if (!defined($cn) or !length($cn)) {
+ notice ($user, "No channel given. The channel name \002must\002 include the # character.");
+ return;
+ }
+
+ if ($args[-1] =~ /\+/) { #expire time is last arguement
+ $expiry = pop @args;
+ $expiry = parse_time($expiry);
+ } else { #expire time is somewhere else (if given), get default expiry for now.
+ $expiry = get_bantime($chan);
+ }
+
+ my @targets;
+
+ foreach my $arg (@args) {
+ if ($arg =~ /\,/) {
+ push @targets, split(/\,/, $arg);
+ next;
+ } else {
+ push @targets, $arg;
+ }
+ }
+
+ my $src = get_user_nick($user);
+
+ my ($bansref, $unbansref) = parse_bans ($user, $chan, '', \@targets, 1, $expiry);
+
+ if ((!$bansref || !scalar @$bansref) && (!$unbansref || !scalar @$unbansref)) {
+ return;
+ }
+
+ if(scalar @$bansref) {
+ tempban ($chan, @$bansref);
+
+ ircd::notice(agent($chan), $cn, "$src used TEMPBAN ".join(' ', @$bansref))
+ if (lc $user->{AGENT} eq lc $csnick) and (cr_chk_flag($chan, CRF_VERBOSE) and scalar(@$bansref));
+ }
+ cs_unban($user, $chan, @$unbansref) if scalar(@$unbansref);
+}
+
sub cs_kick($$$;$$) {
my ($user, $chan, $target, $ban, $reason) = @_;
my $cmd = ($ban ? 'KICKBAN' : 'KICK');
my $perm = ($ban ? 'BAN' : 'KICK');
+
if(ref($chan) ne 'HASH' || !defined($chan->{CHAN})) {
notice($user, "Invalid $cmd command, no channel specified");
return;
);
my @notinchan = ();
my $peace = ({modes::splitmodes(get_modelock($chan))}->{Q}->[0] eq '+');
-
+
my @targets = split(/\,/, $target);
foreach $target (@targets) {
my $tuser = { NICK => $target };
push @{$errors[0]}, $target;
next;
}
-
+
if(get_user_id($tuser)) {
unless(is_in_chan($tuser, $chan)) {
if ($ban) {
push @{$errors[2]}, $target;
next;
}
-
+
if($ban) {
- kickban($chan, $tuser, undef, $reason);
+ kickban($chan, $tuser, undef, $reason, 1);
} else {
ircd::kick(agent($chan), $cn, $target, $reason) unless adminserv::is_service($user);
}
}
-
+ ircd::flushmodes() if($ban);
+
foreach my $errlist (@errors) {
if(@$errlist > 1) {
my $msg = shift @$errlist;
-
+
foreach my $e (@$errlist) { $e = "\002$e\002" }
-
+
notice($user,
"Cannot $cmd ".
enum("or", @$errlist).
sub cs_ban($$$@) {
my ($user, $chan, $type, @targets) = @_;
- my $cn = $chan->{CHAN};
- my $src = get_user_nick($user);
- my $srclevel = get_best_acc($user, $chan);
- my ($nick, $override) = can_do($chan, 'BAN', $user, { ACC => $srclevel });
- return unless $nick;
+ my $src = get_user_nick ($user);
- my @errors = (
- ["I'm sorry, $src, I'm afraid I can't do that."],
- ["User not found"],
- [$err_deny]
- );
-
- my (@bans, @unbans);
- foreach my $target (@targets) {
- my $tuser;
-
- if(ref($target)) {
- $tuser = $target;
- }
- elsif($target =~ /\,/) {
- push @targets, split(',', $target);
- next;
- }
- elsif($target eq '') {
- # Should never happen
- # but it could, given the split above
- next;
- }
- elsif($target =~ /^-/) {
- $target =~ s/^\-//;
- push @unbans, $target;
- next;
- }
-=cut
- elsif($target =~ /[!@]+/) {
- ircd::debug("normalizing hostmask $target");
- #$target = normalize_hostmask($target);
-#=cut
- my ($nick, $ident, $host) = parse_mask($target);
- $nick = '*' unless length($nick);
- $ident = '*' unless length($ident);
- $host = '*' unless length($host);
- $target = "$nick\!$ident\@$host";
-#=cut
- ircd::debug("normalized hostmask: $target");
-
- push @bans, $target;
- next;
- }
-=cut
- elsif(valid_nick($target)) {
- $tuser = { NICK => $target };
- }
- elsif($target = validate_ban($target)) {
- push @bans, $target;
- next;
- }
- my $targetlevel = get_best_acc($tuser, $chan);
-
- if(lc $target eq lc agent($chan) or adminserv::is_service($tuser)) {
- push @{$errors[0]}, get_user_nick($tuser);
- next;
- }
-
- unless(get_user_id($tuser)) {
- push @{$errors[1]}, get_user_nick($tuser);
- next;
- }
- if( $srclevel <= $targetlevel and not ($override && check_override($user, 'BAN', "BAN $cn $target")) ) {
- push @{$errors[2]}, $target;
- next;
- }
+ my $cn = $chan->{CHAN};
- push @bans, make_banmask($chan, $tuser, $type);
- }
+ my ($bansref, $unbansref) = parse_bans ($user, $chan, $type, \@targets);
- foreach my $errlist (@errors) {
- if(@$errlist > 1) {
- my $msg = shift @$errlist;
-
- foreach my $e (@$errlist) { $e = "\002$e\002" }
-
- notice($user,
- "Cannot ban ".
- enum("or", @$errlist).
- ": $msg"
- );
- }
+ if ((!$bansref || !scalar @$bansref) && (!$unbansref || !scalar @$unbansref)) {
+ return;
}
- ircd::ban_list(agent($chan), $cn, +1, 'b', @bans) if (scalar(@bans));
- ircd::notice(agent($chan), $cn, "$src used BAN ".join(' ', @bans))
- if (lc $user->{AGENT} eq lc $csnick) and (cr_chk_flag($chan, CRF_VERBOSE) and scalar(@bans));
- cs_unban($user, $chan, @unbans) if scalar(@unbans);
+ #ircd::ban_list(agent($chan), $cn, +1, 'b', @bans) if (scalar(@bans));
+ tempban($chan, map { [ $_, get_bantime($chan)] } @$bansref);
+ ircd::notice(agent($chan), $cn, "$src used BAN ".join(' ', @$bansref))
+ if (lc $user->{AGENT} eq lc $csnick) and (cr_chk_flag($chan, CRF_VERBOSE) and scalar(@$bansref));
+ cs_unban($user, $chan, @$unbansref) if scalar(@$unbansref);
}
sub cs_invite($$@) {
my @invited;
foreach my $target (@targets) {
my $tuser;
- if(lc($src) eq lc($target)) {
+ my $tnick;
+ if(ref($target)) {
+ $tuser = $target;
+ $tnick = get_user_nick($tuser);
+ } elsif(lc($src) eq lc($target)) {
$tuser = $user;
- }
- elsif($target =~ /\,/) {
+ $tnick = $src;
+ } elsif($target =~ /\,/) {
push @targets, split(',', $target);
next;
- }
- elsif($target eq '') {
+ } elsif($target eq '') {
# Should never happen
# but it could, given the split above
next;
- }
- else {
+ } else {
$tuser = { NICK => $target };
+ $tnick = $target;
}
my $candoOpts = { ACC => $srclevel, NOREPLY => 1, OVERRIDE_MSG => "INVITE $cn $target" };
- if(lc($src) eq lc($target)) {
+ if(lc($src) eq lc($tnick)) {
unless(can_do($chan, 'InviteSelf', $user, $candoOpts)) {
- push @{$errors[2]}, $target;
+ push @{$errors[2]}, $tnick;
next;
}
}
else {
unless(can_do($chan, 'INVITE', $user, $candoOpts)) {
- push @{$errors[2]}, $target;
+ push @{$errors[2]}, $tnick;
next;
}
-
- unless(nickserv::is_online($target)) {
- push @{$errors[0]}, $target;
+
+ unless(nickserv::is_online($tnick)) {
+ push @{$errors[0]}, $tnick;
next;
}
-
+
# invite is annoying, so punish them mercilessly
return if flood_check($user, 2);
}
-
+
if(is_in_chan($tuser, $chan)) {
- push @{$errors[1]}, $target;
+ push @{$errors[1]}, $tnick;
next;
}
-
- ircd::invite(agent($chan), $cn, $target); push @invited, $target;
- ircd::notice(agent($chan), $target, "\002$src\002 has invited you to \002$cn\002.")
- unless(lc($src) eq lc($target));
+
+ ircd::invite(agent($chan), $cn, $tnick); push @invited, $tnick;
+ ircd::notice(agent($chan), $tnick, "\002$src\002 has invited you to \002$cn\002.")
+ unless(lc($src) eq lc($tnick));
}
foreach my $errlist (@errors) {
);
}
}
-
+
ircd::notice(agent($chan), $cn, "$src used INVITE ".join(' ', @invited))
if (lc $user->{AGENT} eq lc $csnick)and cr_chk_flag($chan, CRF_VERBOSE) and scalar(@invited);
}
my $cn = $chan->{CHAN};
my $oper;
- unless($oper = adminserv::is_svsop($user, adminserv::S_ROOT())) {
+ unless($oper = adminserv::can_do($user, 'SERVOP')) {
notice($user, $err_deny);
return;
}
my $cmsg = "is closed [$src $time]: $reason";
if ($type == CRF_CLOSE) {
+ cr_set_flag($chan, CRF_CLOSE, 1); #set flags
clear_users($chan, "Channel $cmsg");
ircd::settopic(agent($chan), $cn, $src, time(), "Channel $cmsg")
}
elsif ($type == CRF_DRONE) {
+ cr_set_flag($chan, CRF_DRONE, 1); #set flags
chan_kill($chan, "$cn $cmsg");
}
notice($user, 'Clear reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
return;
}
-
+
clear_users($chan, "CLEAR USERS by \002$src\002".($reason?" reason: $reason":''));
}
}
sub cs_alist($$;$) {
- my ($user, $chan, $mask) = @_;
+ my ($user, $chan, $mask) = @_;
my $cn = $chan->{CHAN};
chk_registered($user, $chan) or return;
- my $slevel = get_best_acc($user, $chan);
+ my $slevel = get_best_acc($user, $chan);
can_do($chan, 'ACCLIST', $user, { ACC => $slevel }) or return;
my ($user, $chan, @parms) = @_;
my $cn = $chan->{CHAN};
- my $self = 1 if ( (scalar(@parms) == 1) and ( lc($parms[0]) eq lc(get_user_nick($user)) ) );
+ my $self;
+ $self = 1 if ( (scalar(@parms) == 1) and ( lc($parms[0]) eq lc(get_user_nick($user)) ) );
if ($parms[0] eq '*') {
cs_clear_bans($user, $chan);
return;
'v' => 'VOICE',
);
my $sign = '+'; my $cn = $chan->{CHAN};
- my ($modes_out, @parms_out);
+ my ($modes_out, @parms_out, @bans);
foreach my $mode (split(//, $modes_in)) {
$sign = $mode if $mode =~ /[+-]/;
if ($permhash{$mode}) {
my $parm = shift @parms_in;
cs_setmodes($user, ($sign eq '-' ? 'de' : '').$permhash{$mode}, $chan, $parm);
}
- elsif($mode =~ /[beIlLkjf]/) {
+ elsif ($mode eq 'b') {
+ my $parm = shift @parms_in;
+ if($sign eq '-') {
+ $parm = '-'.$parm;
+ }
+ push @bans, $parm;
+ }
+ elsif($mode =~ /[eIlLkjf]/) {
$modes_out .= $mode;
push @parms_out, shift @parms_in;
} else {
}
}
+ if(scalar(@bans)) {
+ cs_ban($user, $chan, undef, @bans);
+ }
return if $modes_out =~ /^[+-]*$/;
ircd::setmode(agent($chan), $chan->{CHAN}, $modes_out, join(' ', @parms_out));
do_modelock($chan, $modes_out.' '.join(' ', @parms_out));
} elsif(!(get_op($user, $chan2) & ($opmodes{o} | $opmodes{a} | $opmodes{q}))) {
# This would be preferred to be a 'opmode_mask' or something
# However that might be misleading due to hop not being enough to register
- notice($user, "You must have channel operator status to register \002$cn2\002.");
+ notice($user, "You must have channel operator status to register \002$cn2\002.");
return;
} else {
cs_copy_chan_all($user, $chan1, $chan2);
# does this need its own privilege now?
can_do($chan, 'SET', $user) or return;
my $modes;
- {
+ if(scalar(@args)) {
my ($modes_in, @parms_in) = validate_chmodes(shift @args, @args);
$modes = $modes_in.' '.join(' ', @parms_in);
@args = undef;
elsif(lc $cmd eq 'set') {
$modes = modes::merge($modes, "+r", 1);
$set_modelock->execute($modes, $cn);
+ }
+ elsif(lc $cmd eq 'reset') {
+ $set_modelock->execute(services_conf_default_channel_mlock, $cn);
} else {
notice($user, "Unknown MLOCK command \"$cmd\"");
+ return;
}
notice($user, "Mode lock for \002$cn\002 has been set to: \002$modes\002");
}
use SrSv::MySQL::Stub {
- getChanUsers => ['COLUMN', "SELECT user.nick FROM chanuser, user
- WHERE chanuser.chan=? AND user.id=chanuser.nickid AND chanuser.joined=1"]
+ getChanUsers => ['COLUMN', "SELECT user.nick FROM chanuser JOIN user ON (user.id=chanuser.nickid)
+ WHERE chanuser.chan=? AND chanuser.joined=1"]
};
sub cs_resync($@) {
my $chan = { CHAN => $cn };
next unless cs_clear_ops($user, $chan, 'Resync');
cs_updown2($user, 'up', $chan, getChanUsers($cn));
+ if(can_do($chan, 'AKickEnforce', $user, { OVERRIDE_MSG => "AKICK $cn ENFORCE", NOREPLY => 1 })) {
+ cs_akick_enforce($user, $chan);
+ }
}
}
+sub cs_join($@) {
+ my ($user, @cns) = @_;
+ my @reply;
+ my @out_cns;
+ foreach my $cn (@cns) {
+ if($cn =~ /,/) {
+ push @cns, split(',', $cn);
+ }
+ elsif($cn eq '') {
+ next;
+ }
+ my $chan = { CHAN => $cn };
+ my $cando_opts = { NOREPLY => 1 };
+ if(check_akick($user, $chan, 1)) {
+ push @reply, "You are banned from $cn";
+ next;
+ } elsif(!can_do($chan, 'JOIN', $user, $cando_opts)) {
+ push @reply, "$cn is a private channel.";
+ next;
+ }
+ if(is_in_chan($user, $chan)) {
+ next;
+ }
+ if(can_do($chan, 'InviteSelf', $user, $cando_opts)) {
+ cs_invite($user, $chan, $user);
+ }
+ push @out_cns, $cn;
+
+ }
+ ircd::svsjoin(get_user_agent($user), get_user_nick($user), @out_cns) if scalar @out_cns;
+ notice($user, @reply) if scalar @reply;
+}
+
+sub cs_topic($$@) {
+ my ($user, $cn, @args) = @_;
+ my ($chan, $msg) = ($cn->{CHAN}, join(" ", @args));
+ can_do($cn, 'SETTOPIC', $user) or return undef;
+ ircd::settopic(agent($cn), $chan, get_user_nick($user), time, ($msg =~ /^none/i ? "" : $msg));
+}
+
### MISCELLANEA ###
# these are helpers and do NOT check if $cn1 or $cn2 is reg'd
return $type."$nick!$ident\@$vhost";
}
-sub kickban($$$$) {
- my ($chan, $user, $mask, $reason) = @_;
+sub kickban($$$$;$$) {
+ my ($chan, $user, $mask, $reason, $noflush) = @_;
+
my $cn = $chan->{CHAN};
- my $nick = get_user_nick($user);
+ my $nick;
+ $nick = get_user_nick($user) if ($user);
+
+ if (!$user && !$mask) {
+ return;
+ }
- return 0 if adminserv::is_service($user);
+ return 0 if $user && adminserv::is_service($user);
my $agent = agent($chan);
}
enforcer_join($chan) if (get_user_count($chan) <= 1);
- ircd::setmode($agent, $cn, '+b', $mask);
- ircd::flushmodes();
- ircd::kick($agent, $cn, $nick, $reason);
+ #ircd::setmode($agent, $cn, '+b', $mask);
+ tempban($chan, [$mask, get_bantime($chan)]);
+
+ ircd::flushmodes() unless $noflush;
+ ircd::kick($agent, $cn, $nick, $reason) if ($nick);
return 1;
}
my ($chan, $users, $reason) = @_;
my $cn = $chan->{CHAN};
my $agent = agent($chan);
-
+
enforcer_join($chan);
ircd::setmode($agent, $cn, '+b', '*!*@*');
ircd::flushmodes();
my $cn = $chan->{CHAN};
my $agent = agent($chan);
my $i;
-
+
enforcer_join($chan);
ircd::setmode($agent, $cn, '+b', '*!*@*');
ircd::flushmodes();
$nick = '%' if ($nick eq '');
$ident = '%' if ($ident eq '');
$host = '%' if ($host eq '');
-
+
if ($ban) {
my $banmask = $nick.'!'.$ident.'@'.$host;
$banmask =~ tr/%_/*?/;
$nick = '%' if ($nick eq '');
$ident = '%' if ($ident eq '');
$host = '%' if ($host eq '');
-
+
if ($ban) {
my $banmask = $nick.'!'.$ident.'@'.$host;
$banmask =~ tr/%_/*?/;
my $agent = agent($chan);
$type = 0 unless defined $type;
my $mode = ($type == 128 ? 'e' : 'b');
-
+
my @banlist = ();
$get_all_bans->execute($cn, $type);
while(my ($mask) = $get_all_bans->fetchrow_array) {
ircd::unban_nick(agent($chan), $cn, @nicklist);
return scalar(@nicklist);
}
-
+
foreach my $tuser (@userlist) {
my $tuid;
unless($tuid = get_user_id($tuser)) {
my ($reason) = $get_nick_akick->fetchrow_array(); $get_nick_akick->finish();
return 0 if adminserv::is_svsop($tuser, adminserv::S_HELP());
+ if(defined($reason) && $reason =~ /\|/) {
+ ($reason, undef) = split(/ ?\| ?/, $reason, 2);
+ }
kickban($chan, $tuser, undef, "User has been banned from ".$cn.($reason?": $reason":''));
}
-sub do_status($$) {
- my ($user, $chan) = @_;
-
- return 0 if cr_chk_flag($chan, (CRF_CLOSE | CRF_DRONE));
-
- my $uid = get_user_id($user);
- my $nick = get_user_nick($user);
- my $cn = $chan->{CHAN};
+sub check_akick($$;$) {
+ my ($user, $chan, $check_only) = @_;
- my ($acc, $root) = get_best_acc($user, $chan, 2);
- if ($acc == -1) {
- do_nick_akick($user, $chan, $root);
+ if(adminserv::is_svsop($user, adminserv::S_HELP())) {
return 0;
}
- unless(can_do($chan, 'JOIN', $user, { ACC => $acc })) {
- kickban($chan, $user, undef, 'This is a private channel.');
- return 0;
+ my ($acc, $root) = get_best_acc($user, $chan, 2);
+ if ($acc == -1) {
+ do_nick_akick($user, $chan, $root) unless $check_only;
+ return 1;
}
-
- unless($acc or adminserv::is_svsop($user, adminserv::S_HELP()) ) {
+ my $cn = $chan->{CHAN};
+ my $uid = get_user_id($user);
+ unless($acc) {
$get_akick->execute($uid, $cn);
if(my @akick = $get_akick->fetchrow_array) {
- akickban($cn, @akick);
+ akickban($cn, @akick) unless $check_only;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+sub do_status($$;$) {
+ my ($user, $chan, $check_only) = @_;
+
+ return 0 if cr_chk_flag($chan, (CRF_CLOSE | CRF_DRONE));
+
+ my $nick = get_user_nick($user);
+
+ if(check_akick($user, $chan, $check_only)) {
+ return 0;
+ }
+ my ($acc, $root) = get_best_acc($user, $chan, 2);
+ my ($accnick, $override) = can_do($chan, 'JOIN', $user, { ACC => $acc, NOREPLY => 1 });
+ unless ($acc > 0 || $override) {
+ if (clones_exist ($user, $chan) && !adminserv::is_service($user)) {
+ my $mask = make_banmask($chan, $user);
+ my $cn = $chan->{CHAN};
+
+ tempban($chan, [ $mask, 60 ]);
+ ircd::kick(agent($chan), $cn, $nick, "No clones allowed in this channel.");
+
return 0;
}
}
- set_modes($user, $chan, $acc, cr_chk_flag($chan, CRF_SPLITOPS, 0))
- if is_registered($chan)
- and not is_neverop_user($user)
- and not cr_chk_flag($chan, (CRF_CLOSE | CRF_DRONE | CRF_NEVEROP));
-
+ if(!$accnick && !$override) {
+ kickban($chan, $user, undef, 'This is a private channel.')
+ unless $check_only;
+ return 0;
+ }
+
+ if( !$check_only && is_registered($chan) &&
+ !cr_chk_flag($chan, (CRF_CLOSE | CRF_DRONE)) )
+ {
+ my $neverop = (is_neverop_user($user) || cr_chk_flag($chan, CRF_NEVEROP, 1));
+ my $no_deop = cr_chk_flag($chan, CRF_SPLITOPS, 0);
+ my $op_anyway = 0;
+ if($neverop && cr_chk_flag($chan, CRF_AUTOVOICE, 1) && $acc > 2) {
+ $acc = 2;
+ $no_deop = 0;
+ $op_anyway = 1;
+ }
+ set_modes($user, $chan, $acc,
+ # $acc == 3 is +h
+ # this probably needs to be configurable for ports
+ # also Unreal may [optionally] set +q on join.
+ $no_deop,
+ !$neverop || $op_anyway,
+ );
+ }
+
return 1;
}
$host =~ tr/\%\_/\*\?/;
}
+ if(defined($reason) && $reason =~ /\|/) {
+ ($reason, undef) = split(/ ?\| ?/, $reason, 2);
+ }
+
return kickban($chan, $target, "$bnick!$ident\@$host", "User has been banned from ".$cn.($reason?": $reason":''));
}
sub fix_private_join_before_id($) {
my ($user) = @_;
- my @cns;
-
- $get_recent_private_chans->execute(get_user_id($user));
- while(my ($cn) = $get_recent_private_chans->fetchrow_array) {
+ my @cns = get_recent_private_chans(get_user_id($user));
+ foreach my $cn (@cns) {
my $chan = { CHAN => $cn };
unban_user($chan, $user);
- push @cns, $cn;
}
ircd::svsjoin($csnick, get_user_nick($user), @cns) if @cns;
sub get_user_count($) {
my ($chan) = @_;
my $cn = $chan->{CHAN};
-
+
$get_user_count->execute($cn);
-
+
return $get_user_count->fetchrow_array;
}
} else {
$cn = $chan;
}
-
+
$get_modelock->execute($cn);
my ($ml) = $get_modelock->fetchrow_array;
$get_modelock->finish();
my $seq = $ircline;
$get_modelock_lock->execute; $get_modelock_lock->finish;
-
+
$get_chanmodes->execute($cn);
my ($omodes) = $get_chanmodes->fetchrow_array;
my $ml = get_modelock($chan);
my $nmodes = modes::add($omodes, $modes, 1);
$ml = modes::diff($nmodes, $ml, 1);
$set_chanmodes->execute(modes::add($nmodes, $ml, 1), $cn);
-
+
return $ml;
}
my ($perm) = @_;
$is_level->execute($perm);
-
+
return $is_level->fetchrow_array;
}
sub is_registered($) {
my ($chan) = @_;
my $cn = $chan->{CHAN};
-
+
$is_registered->execute($cn);
if($is_registered->fetchrow_array) {
return 1;
my ($user) = @_;
my $uid = get_user_id($user);
my @chans;
-
+
$get_user_chans->execute($uid, $ircline, $ircline+1000);
while(my ($chan) = $get_user_chans->fetchrow_array) {
push @chans, $chan;
my ($user) = @_;
my $uid = get_user_id($user);
my (@curchans, @oldchans);
-
+
$get_user_chans_recent->execute($uid);
while(my ($cn, $joined, $op) = $get_user_chans_recent->fetchrow_array) {
if ($joined) {
sub set_acc($$$$) {
my ($nick, $user, $chan, $level) = @_;
my $cn = $chan->{CHAN};
- my $adder = get_best_acc($user, $chan, 1) if $user;
+ my $adder;
+ $adder = get_best_acc($user, $chan, 1) if $user;
$set_acc1->execute($cn, $level, $nick);
$set_acc2->execute($level, $adder, $cn, $nick);
sub set_modes_allnick($$$) {
my ($nick, $chan, $level) = @_;
my $cn = $chan->{CHAN};
-
+
$get_using_nick_chans->execute($nick, $cn);
while(my ($n) = $get_using_nick_chans->fetchrow_array) {
my $user = { NICK => $n };
}
# If channel has OPGUARD, $doneg is true.
-sub set_modes($$$;$) {
- my ($user, $chan, $acc, $doneg) = @_;
+sub set_modes($$$;$$) {
+ my ($user, $chan, $acc, $doneg, $dopos) = @_;
+ # can you say eww?
+ $dopos = 1 unless defined($dopos);
+ $doneg = 0 unless defined($doneg);
my $cn = $chan->{CHAN};
-
+
if ($acc < 0) {
# Do akick stuff here.
}
-
- my $dst = $ops[$acc];
+
+ my $dst = ( $acc > 0 ? $ops[$acc] : 0 );
my $cur = get_op($user, $chan);
my ($pos, $neg);
-
+
if (cr_chk_flag($chan, CRF_FREEZE)) {
set_mode_mask($user, $chan, $cur, undef);
return;
$neg = ($dst ^ $cur) & $cur if $doneg;
if($pos or $neg) {
- set_mode_mask($user, $chan, $neg, $pos);
+ set_mode_mask($user, $chan, ($doneg ? $neg : '-'), ($dopos ? $pos : '+'));
}
if($pos) {
# and checking if an override was used is becoming tricky.
# We had a case in cs_kick where an oper should be able to override +Q/$peace
# but cannot b/c they have regular access in that channel.
- my (undef, $override) = check_override($user, $perm);
+ my $override;
+ if(defined($user)) {
+ (undef, $override) = check_override($user, $perm);
+ }
return (wantarray ? ($nick, $override) : $nick);
} elsif ( $user and adminserv::is_svsop($user, adminserv::S_HELP()) ) {
#set_lastused($cn, get_user_id($user));
# LEVELS affect opguard, the loop will have to be unrolled.
# --
# Only call this if you've already checked opguard, as we do not check it here.
-# --
+# --
# Remember, this isn't a permission check if someone is allowed to op someone [else],
# rather this checks if the person being opped is allowed to keep/have it.
my ($user, $chan, $tuser, $opmode) = @_;
sub get_ban_num($$) {
my ($chan, $num) = @_;
- $get_ban_num->execute($chan->{CHAN}, $num-1);
+ $get_ban_num->execute($chan->{CHAN}, 0, $num-1);
my ($mask) = $get_ban_num->fetchrow_array();
$get_ban_num->finish();
return sql2glob($mask);
foreach my $user (@$users) {
$user->{__ID} = get_user_id($user);
+
unless (defined($user->{__ID})) {
# This does happen occasionally. it's a BUG.
# At least we have a diagnostic for it now.
my $user = { NICK => $src };
my $chan = { CHAN => $cn };
my ($sign, $num);
-
+
# XXX This is not quite right, but maybe it's good enough.
my $mysync = ($src =~ /\./ ? 0 : 1);
if($modes !~ /^[beIvhoaq+-]+$/ and (!synced() or $mysync)) {
do_modelock($chan, "$modes $args");
}
-
+
my $opguard = (!current_message->{SYNC} and cr_chk_flag($chan, CRF_OPGUARD, 1));
my @perms = ('VOICE', 'HALFOP', 'OP', 'PROTECT');
my $unmodes = '-';
my @unargs;
-
+
my @modes = split(//, $modes);
my @args = split(/ /, $args);
$num = 2 if $mode eq 'o';
$num = 3 if $mode eq 'a';
$num = 4 if $mode eq 'q';
-
+
if($opguard and $sign == 1 and
!can_keep_op($user, $chan, $auser, $mode)
) {
sub process_ban($$$$) {
my ($cn, $arg, $src, $type, $sign) = @_;
-
+
+ my $arg2 = $arg;
+
$arg =~ tr/\*\?/\%\_/;
-
+
if ($sign > 0) {
$add_ban->execute($cn, $arg, $src, $type);
} else {
$delete_ban->execute($cn, $arg, $type);
+ del_tempban($cn, $arg2);
}
}
-
+sub cs_topicappend {
+ my ($user, $cn, $topicappend) = @_;
+ $get_topic->execute($cn);
+ my ($ntopic, $nsetter, $ntime) = $get_topic->fetchrow_array;
+ my $newtopic;
+ if ($ntopic) {
+ $newtopic = $ntopic . " | " . $topicappend;
+ }
+ else { $newtopic = $topicappend; }
+ cs_topic ($user, { CHAN => $cn }, $newtopic);
+}
+sub cs_topicprepend {
+ my ($user, $cn, $topicprepend) = @_;
+ $get_topic->execute($cn);
+ my ($ntopic, $nsetter, $ntime) = $get_topic->fetchrow_array;
+ my $newtopic;
+ if ($ntopic) {
+ $newtopic = $topicprepend . " | " . $ntopic;
+ }
+ else { $newtopic = $topicprepend; }
+ cs_topic ($user, { CHAN => $cn }, $newtopic);
+}
sub chan_topic {
my ($src, $cn, $setter, $time, $topic) = @_;
my $chan = { CHAN => $cn };
my $suser = { NICK => $setter, AGENT => agent($chan) };
+ return unless is_registered($chan);
return if cr_chk_flag($chan, CRF_CLOSE, 1);
-
+
if(current_message->{SYNC}) { # We don't need to undo our own topic changes.
$set_topic1->execute($setter, $time, $cn);
$set_topic2->execute($cn, $topic);
if(!synced()) {
$get_topic->execute($cn);
my ($ntopic, $nsetter, $ntime) = $get_topic->fetchrow_array;
- if($topic ne '' and $time == $ntime or can_do($chan, 'SETTOPIC', undef)) {
+ if($topic ne '' and $time == $ntime or can_do($chan, 'SETTOPIC', undef, { ACC => 0 })) {
$set_topic1->execute($setter, $time, $cn);
$set_topic2->execute($cn, $topic);
} else {
ircd::settopic(agent($chan), $cn, $nsetter, $ntime, $ntopic);
}
}
-
+
elsif(lc($src) ne lc($setter) or can_do($chan, 'SETTOPIC', $suser)) {
$set_topic1->execute($setter, $time, $cn);
$set_topic2->execute($cn, $topic);
sub eos(;$) {
my ($server) = @_;
my $gsa;
-
+
$get_all_closed_chans->execute(CRF_DRONE|CRF_CLOSE);
while(my ($cn, $type, $reason, $opnick, $time) = $get_all_closed_chans->fetchrow_array) {
my $chan = { CHAN => $cn };
-
+
my $cmsg = " is closed [$opnick ".gmtime2($time)."]: $reason";
if($type == CRF_DRONE) {
chan_kill($chan, $cn.$cmsg);
clear_users($chan, "Channel".$cmsg);
}
}
-
+
while($chanuser_table > 0) { }
-
+
$get_eos_lock->execute(); $get_eos_lock->finish;
$get_akick_all->execute();
if($server) {
#next if chk_user_flag($user, UF_FINISHED);
$agent = $csnick unless $agent;
my $chan = { CHAN => $cn, FLAGS => $cflags, AGENT => $agent };
-
+
set_modes($user, $chan, $level, ($cflags & CRF_OPGUARD)) if not $neverop and $ops[$level] != $op and not $cflags & (CRF_FREEZE | CRF_CLOSE | CRF_DRONE);
do_welcome($user, $chan);
}
set_user_flag_all(UF_FINISHED());
$unlock_tables->execute(); $unlock_tables->finish;
+ check_expired_bans();
}
1;
use SrSv::MySQL '$dbh';
use SrSv::MySQL::Glob;
+require SrSv::DB::StubGen;
+
our $hsnick_default = 'HostServ';
our $hsnick = $hsnick_default;
-our (
- $set_vhost, $get_vhost, $del_vhost,
- $vhost_chgroot,
-
- $get_matching_vhosts
-);
-
sub init() {
- $set_vhost = $dbh->prepare("REPLACE INTO vhost SELECT id, ?, ?, ?, UNIX_TIMESTAMP() FROM nickreg WHERE nick=?");
- $get_vhost = $dbh->prepare("SELECT vhost.ident, vhost.vhost FROM vhost, nickalias WHERE nickalias.nrid=vhost.nrid AND nickalias.alias=?");
- $del_vhost = $dbh->prepare("DELETE FROM vhost USING vhost, nickreg WHERE nickreg.nick=? AND vhost.nrid=nickreg.id");
+import SrSv::DB::StubGen (
+ dbh => $dbh,
+ generator => 'services_mysql_stubgen',
+);
- $get_matching_vhosts = $dbh->prepare("SELECT nickreg.nick, vhost.ident, vhost.vhost, vhost.adder, vhost.time FROM
- vhost, nickreg WHERE vhost.nrid=nickreg.id AND nickreg.nick LIKE ? AND vhost.ident LIKE ? AND vhost.vhost LIKE ?
- ORDER BY nickreg.nick");
+services_mysql_stubgen(
+ [set_vhost => 'INSERT', "REPLACE INTO vhost SELECT id, ?, ?, ?, UNIX_TIMESTAMP() FROM nickreg WHERE nick=?"],
+ [get_vhost => 'ROW', "SELECT vhost.ident, vhost.vhost
+ FROM vhost, nickalias
+ WHERE nickalias.nrid=vhost.nrid AND nickalias.alias=?"],
+ [del_vhost => 'NULL', "DELETE FROM vhost USING vhost, nickreg WHERE nickreg.nick=? AND vhost.nrid=nickreg.id"],
+ [get_matching_vhosts => 'ARRAY', "SELECT nickreg.nick, vhost.ident, vhost.vhost, vhost.adder, vhost.time
+ FROM vhost JOIN nickreg ON (vhost.nrid=nickreg.id)
+ WHERE nickreg.nick LIKE ? AND vhost.ident LIKE ? AND vhost.vhost LIKE ?
+ ORDER BY nickreg.nick"],
+);
}
sub dispatch($$$) {
return;
}
- $get_vhost->execute($nick);
- my ($vident, $vhost) = $get_vhost->fetchrow_array;
+ my ($vident, $vhost) = get_vhost($nick);
unless ($vhost) {
notice($user, "You don't have a vHost.") unless $identify;
return;
($vident, $vhost) = split(/\@/, $vhost);
}
my $src = get_user_nick($user);
- $set_vhost->execute($vident, $vhost, $src, $rootnick);
+ set_vhost($vident, $vhost, $src, $rootnick);
notice($user, "vHost for \002$target ($rootnick)\002 set to \002".($vident?"$vident\@":'')."$vhost\002");
}
return;
}
- $del_vhost->execute($rootnick);
+ del_vhost($rootnick);
notice($user, "vHost for \002$target ($rootnick)\002 deleted.");
}
$mhost = '%' if($mhost eq '');
my @data;
- $get_matching_vhosts->execute($mnick, $mident, $mhost);
- while(my ($rnick, $vident, $vhost) = $get_matching_vhosts->fetchrow_array) {
+ foreach my $vhostEnt (get_matching_vhosts($mnick, $mident, $mhost)) {
+ my ($rnick, $vident, $vhost) = @$vhostEnt;
push @data, [$rnick, ($vident?"$vident\@":'').$vhost];
}
package memoserv;
use strict;
-use DBI qw(:sql_types);
#use constant {
# READ => 1,
# DEL => 2,
use SrSv::Text::Format qw(columnar);
use SrSv::Errors;
-use SrSv::User qw(get_user_nick get_user_id :flood);
+use SrSv::User qw(get_user_nick get_user_id get_user_agent :flood);
use SrSv::User::Notice;
use SrSv::Help qw( sendhelp );
+use SrSv::Conf2Consts qw( main );
+
use SrSv::NickReg::Flags;
use SrSv::NickReg::User qw(is_identified get_nick_user_nicks);
-use SrSv::MySQL '$dbh';
+use SrSv::MySQL qw( $dbh :sql_types );
-use SrSv::Util qw( makeSeqList );
+use SrSv::Util qw( makeSeqList seqifyList );
use constant (
MAX_MEMO_LEN => 400
my $user = { NICK => $src, AGENT => $dst };
return if flood_check($user);
+ if($SrSv::IRCd::State::queue_depth > main_conf_queue_highwater && !adminserv::is_svsop($user)) {
+ notice($user, get_user_agent($user)." is too busy right now. Please try your command again later.");
+ }
if($cmd =~ /^send$/i) {
if(@args >= 2) {
if(my ($from, $chan, $time) = $get_memo->fetchrow_array) {
$delete_memo->execute($from, $root, $chan, $time);
push @deleted, $num;
- } else {
- push @notDeleted, $num;
+ } else {
+ push @notDeleted, $num;
}
}
if(scalar(@deleted)) {
my $plural = (scalar(@deleted) == 1);
- my $msg = sprintf("Memo%s deleted: ".join(', ', @deleted), ($plural ? '' : 's'));
+ my $msg = sprintf("Memo%s deleted: ".join(', ', seqifyList @deleted), ($plural ? '' : 's'));
notice($user, $msg);
}
if(scalar(@notDeleted)) {
- my $msg = sprintf("Memos not found: ".join(', ', @notDeleted));
+ my $msg = sprintf("Memos not found: ".join(', ', seqifyList @notDeleted));
notice($user, $msg);
}
-}
+}
sub ms_ignore_add($$) {
my ($user, $nick) = @_;
use strict;
use Time::Local;
-use DBI qw(:sql_types);
-
use SrSv::Timer qw(add_timer);
use SrSv::IRCd::State qw($ircline synced initial_synced %IRCd_capabilities);
use SrSv::Agent;
use SrSv::Conf2Consts qw(main services sql);
use SrSv::HostMask qw(normalize_hostmask hostmask_to_regexp parse_mask parse_hostmask make_hostmask);
-use SrSv::MySQL '$dbh';
+use SrSv::MySQL qw( $dbh :sql_types );
use SrSv::MySQL::Glob;
use SrSv::Shared qw(%newuser %olduser);
use SrSv::Util qw( makeSeqList );
+use SrSv::Debug;
+
+use SrSv::NickReg::NickText;
+
+use SrSv::IPv6;
+
require SrSv::MySQL::Stub;
use constant {
# It is 2**24-1
MAX_LIM => 16777215,
- NTF_QUIT => 1,
- NTF_GREET => 2,
- NTF_JOIN => 3,
- NTF_AUTH => 4,
- NTF_UMODE => 5,
- NTF_VACATION => 6,
- NTF_AUTHCODE => 7,
- NTF_PROFILE => 8,
-
# This could be made a config option
# But our config system currently sucks.
MAX_PROFILE => 10,
$get_matching_nicks,
$cleanup_nickid, $cleanup_users, $cleanup_chanuser,
+ $get_dead_users,
+
$get_expired, $get_near_expired, $set_near_expired,
$get_watches, $check_watch, $set_watch, $del_watch, $drop_watch,
$set_vacation_ntf, $get_vacation_ntf,
$set_authcode_ntf, $get_authcode_ntf,
+
+ $get_nicks_by_email,
);
sub init() {
$is_alias_of = $dbh->prepare("SELECT 1 FROM nickalias AS n1 LEFT JOIN nickalias AS n2 ON n1.nrid=n2.nrid
WHERE n1.alias=? AND n2.alias=? LIMIT 1");
- $get_guest = $dbh->prepare("SELECT guest FROM user WHERE nick=?");
- $set_guest = $dbh->prepare("UPDATE user SET guest=? WHERE nick=?");
+ $get_guest = $dbh->prepare("SELECT flags & @{[UF_GUEST]} FROM user WHERE nick=?");
+ $set_guest = $dbh->prepare("UPDATE user SET flags = IF(?, flags | @{[UF_GUEST]}, flags & ~@{[UF_GUEST]})
+ WHERE nick=?");
$get_lock = $dbh->prepare("SELECT GET_LOCK(?, 10)");
$release_lock = $dbh->prepare("SELECT RELEASE_LOCK(?)");
(nicktext.nrid=nickreg.id AND nicktext.type=".NTF_QUIT.")");
$set_ident = $dbh->prepare("UPDATE user SET ident=? WHERE id=?");
$set_vhost = $dbh->prepare("UPDATE user SET vhost=? WHERE id=?");
- $set_ip = $dbh->prepare("UPDATE user SET ip=? WHERE id=?");
+ $set_ip = $dbh->prepare("UPDATE user SET ip=?, ipv6=? WHERE id=?");
$update_regnick_vhost = $dbh->prepare("UPDATE nickreg,nickid SET nickreg.vhost=?
WHERE nickreg.id=nickid.nrid AND nickid.id=?");
$get_regd_time = $dbh->prepare("SELECT nickreg.regd FROM nickreg, nickalias
$create_alias = $dbh->prepare("INSERT INTO nickalias SELECT id, ?, NULL, NULL FROM nickreg WHERE nick=?");
$drop = $dbh->prepare("DELETE FROM nickreg WHERE nick=?");
-
+
$get_aliases = $dbh->prepare("SELECT nickalias.alias FROM nickalias, nickreg WHERE
nickalias.nrid=nickreg.id AND nickreg.nick=? ORDER BY nickalias.alias");
$get_glist = $dbh->prepare("SELECT nickalias.alias, nickalias.protect, nickalias.last
$delete_alias = $dbh->prepare("DELETE FROM nickalias WHERE alias=?");
$delete_aliases = $dbh->prepare("DELETE FROM nickalias USING nickreg, nickalias WHERE
nickalias.nrid=nickreg.id AND nickreg.nick=?");
-
+
$get_all_access = $dbh->prepare("SELECT chanacc.chan, chanacc.level, chanacc.adder, chanacc.time FROM nickalias, chanacc WHERE chanacc.nrid=nickalias.nrid AND nickalias.alias=? ORDER BY chanacc.chan");
$del_all_access = $dbh->prepare("DELETE FROM chanacc USING chanacc, nickreg WHERE chanacc.nrid=nickreg.id AND nickreg.nick=?");
$unlock_tables = $dbh->prepare("UNLOCK TABLES");
$get_matching_nicks = $dbh->prepare("SELECT nickalias.alias, nickreg.nick, nickreg.ident, nickreg.vhost FROM nickalias, nickreg WHERE nickalias.nrid=nickreg.id AND nickalias.alias LIKE ? AND nickreg.ident LIKE ? AND nickreg.vhost LIKE ? LIMIT 50");
-
+
$cleanup_chanuser = $dbh->prepare("DELETE FROM chanuser USING chanuser
LEFT JOIN user ON (chanuser.nickid=user.id) WHERE user.id IS NULL;");
- $cleanup_nickid = $dbh->prepare("DELETE FROM nickid, user USING nickid LEFT JOIN user ON(nickid.id=user.id) WHERE user.id IS NULL OR (user.online=0 AND quittime<?)");
- $cleanup_users = $dbh->prepare("DELETE FROM user WHERE online=0 AND quittime<?");
+ $cleanup_nickid = $dbh->prepare("DELETE FROM nickid USING nickid
+ LEFT JOIN user ON(nickid.id=user.id)
+ WHERE user.id IS NULL");
+ $cleanup_users = $dbh->prepare("DELETE FROM user WHERE online=0 AND quittime>0 AND quittime<?");
+ $get_dead_users = $dbh->prepare("SELECT id,nick,time,online,quittime FROM user
+ WHERE online=0 AND quittime>0 AND quittime<?");
$get_expired = $dbh->prepare("SELECT nickreg.nick, nickreg.email, nickreg.ident, nickreg.vhost
FROM nickreg LEFT JOIN nickid ON(nickreg.id=nickid.nrid)
$get_authcode_ntf = $dbh->prepare("SELECT 1 FROM nickalias, nicktext
WHERE nicktext.nrid=nickalias.nrid AND nicktext.type=".NTF_AUTHCODE()." AND nickalias.alias=? AND nicktext.data=?");
+ $get_nicks_by_email = $dbh->prepare("SELECT nickreg.nick, nickreg.ident, nickreg.vhost FROM nickreg
+ WHERE nickreg.email LIKE ? GROUP BY nickreg.nick");
+
}
import SrSv::MySQL::Stub {
add_profile_ntf => ['INSERT', "REPLACE INTO nicktext SELECT nickreg.id, @{[NTF_PROFILE]}, 0, ?, ?
FROM nicktext
JOIN nickalias ON (nicktext.nrid=nickalias.nrid)
WHERE nicktext.type=@{[NTF_JOIN]} AND nickalias.alias=?"],
+ wipe_autojoin_ntf => ['NULL', "DELETE nicktext.* FROM nickreg
+ JOIN nickalias ON (nickalias.nrid=nickreg.id)
+ JOIN nicktext ON (nicktext.nrid=nickreg.id)
+ WHERE nicktext.type=@{[NTF_JOIN]} AND nickalias.alias=?"],
del_autojoin_ntf => ['NULL', "DELETE nicktext.* FROM nickreg
JOIN nickalias ON (nickalias.nrid=nickreg.id)
JOIN nicktext ON (nicktext.nrid=nickreg.id)
### NICKSERV COMMANDS ###
sub ns_ajoin_list($$) {
- my ($user, $nick)=@_;
+ my ($user, $nick) = @_;
my @data;
my $i = 0;
foreach my $chan (get_autojoin_ntf($nick)) {
}
sub ns_ajoin_del($$@) {
my ($user, $nick, @args) = @_;
+ my ($subj, $obj);
+ if(lc(get_user_nick($user)) eq lc($nick)) {
+ $subj='your';
+ $obj='you';
+ } else {
+ $subj="\002$nick\002\'s";
+ $obj="\002$nick\002";
+ }
my @entries;
foreach my $arg (@args) {
if ($arg =~ /^[0-9\.,-]+$/) {
if(my $chan = get_autojoin_by_num($nick, $num - 1)) {
push @entries, $chan;
} else {
- notice($user, "No entry \002#$num\002 was found in your ajoin list");
+ notice($user, "No entry \002#$num\002 was found in $subj ajoin list");
}
}
+ } elsif($arg =~ /^#.*?,#/) {
+ push @entries, split(',', $arg);
} else {
push @entries, $arg;
}
foreach my $entry (@entries) {
if(check_autojoin_ntf($nick, $entry)) {
del_autojoin_ntf($nick, $entry);
- notice($user,"Successfully removed \002$entry\002 from your ajoin list.");
+ notice($user,"Successfully removed \002$entry\002 from $subj ajoin list.");
}
else {
- notice($user, "\002$entry\002 was not in your ajoin!");
+ notice($user, "\002$entry\002 was not in $subj ajoin!");
}
}
}
+sub ns_ajoin_wipe($$) {
+ my ($user, $nick) = @_;
+ my ($subj, $obj);
+ if(lc(get_user_nick($user)) eq lc($nick)) {
+ $subj='your';
+ $obj='you';
+ } else {
+ $subj="\002$nick\002\'s";
+ $obj="\002$nick\002";
+ }
+ my $count = wipe_autojoin_ntf($nick);
+ if($count) {
+ notice($user,"Successfully wiped \002$count\002 entries from $subj ajoin list.");
+ } else {
+ notice($user,"No entries deleted.");
+ }
+}
+
+sub ns_ajoin_join($$) {
+ my ($user, $nick) = @_;
+ #ns_ajoin_list($user, $nick);
+ do_ajoin($user, $nick);
+}
-sub ns_ajoin($$@) {
- my ($user, $cmd, @args) = @_;
+sub ns_ajoin($@) {
+ my ($user, @args) = @_;
+ my $nick;
my $src = get_user_nick($user);
- if(!is_identified($user, $src)) {
+ my @chans = grep(/^(#|\d)/, @args);
+ my @parms = grep(!/^(#|\d)/, @args);
+ if(scalar(@parms) > 1) {
+ $nick = shift @parms;
+ } else {
+ $nick = $src;
+ }
+ my $cmd = shift @parms;
+ my ($subj, $obj);
+ if(lc($src) eq lc($nick)) {
+ $subj='Your';
+ $obj='You';
+ } else {
+ $subj="\002$nick\002\'s";
+ $obj="\002$nick\002";
+ }
+
+ my $override = adminserv::can_do($user, 'SERVOP');
+ if(is_identified($user, $nick) || $override) {
if(!is_registered($src)) {
- notice($user, "\002$src\002 is not registered.");
- } else {
- notice($user, "Permission denied for \002$src\002");
+ notice($user, "\002$nick\002 is not registered.");
+ return;
}
+ } else {
+ notice($user, "Permission denied for \002$nick\002");
+ return;
}
if ($cmd =~ /^add$/i) {
- if(!scalar(@args)) {
+ if(!scalar(@chans)) {
notice($user, "Syntax: \002AJOIN ADD #channel\002");
notice ($user, "Type \002/msg NickServ HELP AJOIN\002 for more help");
}
- foreach my $chan (@args) {
- if (defined($chan) && $chan !~ /^#/) {
- $chan = "#" . $chan;
+ foreach my $chanlist (@chans) {
+ if (defined($chanlist) && $chanlist !~ /^#/) {
+ $chanlist = "#" . $chanlist;
}
- if(check_autojoin_ntf($src, $chan)) {
- notice ($user, $chan . " is already in your ajoin list! ");
- next;
- } else {
- add_autojoin_ntf($chan, $src);
- notice($user, "\002$chan\002 added to your ajoin.");
+ foreach my $chan (split(',', $chanlist)) {
+ if(check_autojoin_ntf($nick, $chan)) {
+ notice ($user, $chan . " is already in $subj ajoin list! ");
+ next;
+ } else {
+ add_autojoin_ntf($chan, $nick);
+ notice($user, "\002$chan\002 added to $subj ajoin.");
+ }
}
}
}
elsif ($cmd =~ /^list$/i) {
- ns_ajoin_list($user, $src);
+ ns_ajoin_list($user, $nick);
+ }
+ elsif ($cmd =~ /^join$/i) {
+ ns_ajoin_join($user, $nick);
}
elsif ($cmd =~ /^del(ete)?$/i) {
- ns_ajoin_del($user, $src, @args);
+ ns_ajoin_del($user, $nick, @chans);
+ }
+ elsif ($cmd =~ /^(clear|wipe)$/i) {
+ ns_ajoin_wipe($user, $nick);
}
else {
- notice($user,"Syntax: AJOIN ADD/DEL/LIST");
+ notice($user,"Syntax: AJOIN ADD/DEL/LIST/WIPE");
notice ($user,"Type \002/msg NickServ HELP AJOIN\002 for more help!");
}
}
+our %high_priority_cmds = (
+ 'id' => 1,
+ 'identify' => 1,
+ 'sid' => 1,
+ 'sidentify' => 1,
+ 'gidentify' => 1,
+ 'ghost' => 1,
+);
+
sub dispatch($$$) {
my ($src, $dst, $msg) = @_;
$msg =~ s/^\s+//;
return if flood_check($user);
+ if(!defined($high_priority_cmds{lc $cmd}) &&
+ !adminserv::is_svsop($user) &&
+ $SrSv::IRCd::State::queue_depth > main_conf_queue_highwater)
+ {
+ notice($user, get_user_agent($user)." is too busy right now. Please try your command again later.");
+ return;
+ }
+
if($cmd =~ /^help$/i) {
sendhelp($user, 'nickserv', @args)
}
elsif($cmd =~ /^profile$/i) {
ns_profile($user, @args);
}
+ elsif($cmd =~ /^liste?mail/i) {
+ if ($#args == 0) {
+ ns_listemail($user, $args[0]);
+ } else {
+ notice($user, 'Syntax: LISTEMAIL <email@domain.tld>');
+ }
+ }
else {
notice($user, "Unrecognized command.", "For help, type: \002/msg nickserv help\002");
wlog($nsnick, LOG_DEBUG(), "$src tried to use NickServ $msg");
my ($user, $target, $cmd, $mask, @args) = @_;
my ($expiry, $comment);
my $src = get_user_nick($user);
+ my ($subj, $obj);
+ if(lc(get_user_nick($user)) eq lc($target)) {
+ $subj='your';
+ $obj='you';
+ } else {
+ $subj="\002$target\002\'s";
+ $obj="\002$target\002";
+ }
sub get_silence_by_num($$) {
# This one cannot be converted to SrSv::MySQL::Stub, due to bind_param call
}
my $root = get_root_nick($target);
- unless ($root) {
- notice($user, "\002$target\002 is not registered.");
- return;
+ my $isRegistered;
+ if(!defined($root)) {
+ #notice($user, "\002$target\002 is not registered.");
+ $isRegistered = 0;
+ #return;
+ } else {
+ $isRegistered = 1;
}
- unless(is_identified($user, $target)) {
+ if($isRegistered && !is_identified($user, $target)) {
notice($user, $err_deny);
return;
}
if($mask !~ /[!@.]/) {
my $target_user = { NICK => $mask };
- unless(get_user_id($target_user)) {
+ if(!defined($mask) || !length($mask)) {
+ notice($user, qq{Did not specify a user or hostmask.});
+ return;
+ }
+ elsif(!get_user_id($target_user)) {
notice($user, qq{"\002$mask\002" is not a known user, nor a valid hostmask.});
return;
}
$mask = normalize_hostmask($mask);
}
+=cut
if("$nsnick!services\@".main_conf_local =~ hostmask_to_regexp($mask)) {
notice($user, "You shouldn't add NickServ to your SILENCE list.");
return;
}
+=cut
- $check_silence->execute($root, $mask);
- if ($check_silence->fetchrow_array) {
- notice($user, "\002$mask\002 is already in \002$target\002's SILENCE list.");
- return;
- }
-
if(defined $expiry) {
$expiry = parse_time($expiry) + time();
}
else {
$expiry = 0;
};
- $set_silence->execute($mask, time(), $expiry, $comment, $root);
+ if($isRegistered) {
+ $check_silence->execute($root, $mask);
+ if ($check_silence->fetchrow_array) {
+ notice($user, "\002$mask\002 is already in $subj SILENCE list.");
+ return;
+ }
+
+ $set_silence->execute($mask, time(), $expiry, $comment, $root);
+ }
ircd::svssilence($nsnick, $src, "+$mask");
- notice($user, "\002$mask\002 added to \002$target\002's SILENCE list.");
+ notice($user, "\002$mask\002 added to $subj SILENCE list.");
}
elsif ($cmd =~ /^del(ete)?$/i) {
my @masks;
$check_silence->execute($root, $mask);
unless ($check_silence->fetchrow_array) {
- push @reply, "\002$mask\002 is not in \002$target\002's SILENCE list.";
+ push @reply, "\002$mask\002 is not in $subj SILENCE list.";
next;
}
}
$del_silence->execute($root, $mask);
push @out_masks, "-$mask";
- push @reply, "\002$mask\002 removed from \002$target\002's SILENCE list.";
+ push @reply, "\002$mask\002 removed from $subj SILENCE list.";
}
ircd::svssilence($nsnick, $src, @out_masks);
notice($user, @reply);
$i++;
}
- notice($user, "SILENCE list for \002$target\002:", (scalar @reply ? @reply : " list empty"));
+ notice($user, "SILENCE list for $obj:", (scalar @reply ? @reply : " list empty"));
}
else {
notice($user, 'Syntax: SILENCE [nick] <ADD|DEL|LIST> [mask] [+expiry] [comment]');
my $root = get_root_nick($target);
my $log_str = ($old?'move':'addition')." \002$root\002"
- . ($old ? ' from the '.$chanserv::levels[$old] : '') .
- ' to the '.$chanserv::levels[$level]." list of \002$cn\002";
+ . ($old ? ' from the '.$chanserv::plevels[$old+$chanserv::plzero] : '') .
+ ' to the '.$chanserv::plevels[$level+$chanserv::plzero]." list of \002$cn\002";
services::ulog($chanserv::csnick, LOG_INFO(), "declined the $log_str from $adder", $user, $chan);
notice($user, "You have declined $log_str");
$del_auth->execute($target, $cn);
notice($user, "Profile for \002$target\002 wiped.");
}
+sub ns_listemail($$) {
+ my ($user, $email) = @_;
+ unless(adminserv::is_svsop($user, adminserv::S_HELP())) {
+ notice($user, $err_deny);
+ return;
+ }
+ my $likeemail = glob2sql($email);
+ my (@found, $count);
+
+ $get_nicks_by_email->execute($likeemail);
+ while (my ($nick, $ident, $host) = $get_nicks_by_email->fetchrow_array) {
+ push @found, " $nick ($ident\@$host)";
+ }
+ $email =~ s/\%/\*/g;
+ if ($#found >= 0) {
+ notice($user, "Nicks matching an email address consisting of \002$email\002");
+ for(@found) {
+ notice($user, $_);
+ $count++;
+ }
+ notice($user, "Found \002$count\002 matching nicks.");
+ } else {
+ notice($user, "There were no nicknames registered with an email address consisting of \002$email\002");
+ }
+}
### MISCELLANEA ###
}
}
-sub kill_user($$) {
- my ($user, $reason) = @_;
-
- ircd::irckill(get_user_agent($user) || main_conf_local, get_user_nick($user), $reason);
-}
-
-sub kline_user($$$) {
- my ($user, $time, $reason) = @_;
- my $agent = get_user_agent($user);
- my ($ident, $host) = get_host($user);
-
- ircd::kline($agent, '*', $host, $time, $reason);
+sub do_ajoin($$) {
+ my ($user, $nick) = @_;
+ my $src = get_user_nick($user);
+ if(my @chans = get_autojoin_ntf($nick)) {
+ chanserv::cs_join($user, @chans);
+ }
}
sub do_identify ($$$;$$) {
hostserv::hs_on($user, $root, 1);
- if(my @chans = get_autojoin_ntf($nick)) {
- ircd::svsjoin($nsnick, $src, @chans);
- }
nickserv::do_svssilence($user, $root);
nickserv::do_svswatch($user, $root);
elsif(defined $umodes) {
ircd::setumode($nsnick, $src, $umodes);
}
+ do_ajoin($user, $nick);
return ($enforced ? 2 : 1);
}
return $set_ident->execute($ident, $id);
}
+sub set_ipv6($$$) {
+ my ($user, $ip, $ipv6) = @_;
+ my $id = get_user_id($user);
+
+ return $set_ip->execute($ip, $ipv6, $id);
+}
sub set_ip($$) {
my ($user, $ip) = @_;
my $id = get_user_id($user);
- return $set_ip->execute($ip, $id);
+ return $set_ip->execute($ip, undef, $id);
}
sub get_root_nick($) {
$memoserv::wipe_ignore->execute($root);
$memoserv::purge_ignore->execute($root);
chanserv::drop_nick_chans($root);
- $hostserv::del_vhost->execute($root);
+ hostserv::del_vhost($root);
$drop_watch->execute($root);
$drop_silence->execute($root);
$drop_nicktext->execute($root);
sub cleanup_users() {
add_timer('', services_conf_old_user_age, __PACKAGE__, 'nickserv::cleanup_users');
+ if(DEBUG) {
+ ircd::privmsg('ServServ', main_conf_diag, "Starting cleanup_users()");
+ }
+
my $time = (time() - (services_conf_old_user_age * 2));
- $cleanup_users->execute($time);
- $cleanup_nickid->execute($time);
+ if(DEBUG) {
+ $get_dead_users->execute($time);
+ my $arrayRef = $get_dead_users->fetchall_arrayref();
+ if($arrayRef && scalar(@$arrayRef)) {
+ ircd::privmsg('ServServ', main_conf_diag, columnar( { BORDER => 1, NOHIGHLIGHT => 1 }, @$arrayRef ) );
+ }
+ $get_dead_users->finish();
+ }
+ my $rows = $cleanup_users->execute($time) + 0;
+ $cleanup_nickid->execute();
$cleanup_chanuser->execute();
+ if(DEBUG) {
+ ircd::privmsg('ServServ', main_conf_diag, "Deleted $rows dead users\n");
+ ircd::privmsg('ServServ', main_conf_diag, "Ending cleanup_users()");
+ }
}
sub fix_vhosts() {
if($new =~ /^guest/i) {
$get_guest->execute($new);
- if($get_guest->fetchrow_array) {
+ my ($guest) = $get_guest->fetchrow_array();
+ if($guest) {
$set_guest->execute(0, $new);
} else {
guestnick($new);
# awaiting resolution UnrealIRCd bug 2613
elsif ($modelist{t} eq '-') {
my %omodelist = modes::splitumodes($omodes);
- if($omodelist->{x} eq '+') {
+ if($omodelist{x} eq '+') {
my (undef, $cloakhost) = get_cloakhost($user);
if($cloakhost) {
do_chghost(undef, $nick, $cloakhost, 1);
sub userip($$$) {
my($src, $nick, $ip) = @_;
+ my $is_ipv6;
+ ($is_ipv6, $ip) = is_ipv6($ip);
my $user = { 'NICK' => $nick };
my $new = $newuser{lc $nick};
delete $newuser{lc $nick};
#my $targetid = get_nick_id($target);
- my $iip; my @ips = split(/\./, $ip);
- for(my $i; $i < 4; $i++) {
- $iip += $ips[$i] * (2 ** ((3 - $i) * 8));
+ my $iip;
+ if(!$is_ipv6) {
+ my @ips = split(/\./, $ip);
+ for(my $i; $i < 4; $i++) {
+ $iip += $ips[$i] * (2 ** ((3 - $i) * 8));
+ }
+ } else {
+ $iip = Socket6::inet_pton(&AF_INET6, $ip);
}
get_lock($nick);
-
+
my $id = get_user_id($user);
- set_ip($user, $iip);
+ if(!$is_ipv6) {
+ set_ip($user, $iip);
+ } else {
+ $iip = get_ipv6_net($ip);
+ set_ipv6($user, $iip, $ip);
+ }
my $killed = kill_clones($user, $iip);
release_lock($nick);
use SrSv::Conf2Consts qw(main services);
-use SrSv::User qw(get_user_nick get_user_id get_user_agent is_online get_user_info :flood);
+use SrSv::User qw(get_user_nick get_user_id get_user_agent is_online get_user_info get_user_ip :flood);
use SrSv::User::Notice;
use SrSv::Help qw( sendhelp );
use SrSv::MySQL '$dbh';
+use SrSv::IPv6;
+
use constant {
MAX_LIM => 16777215
};
$get_clones_fromhost, $get_clones_fromnick, $get_clones_fromid, $get_clones_fromipv4,
- $get_session_list
+ $get_session_list,
+
+ $get_newusers, $get_newusers_noid
);
sub init() {
WHERE clone.ip=INET_ATON(?) GROUP BY id");
$get_session_list = $dbh->prepare("SELECT host, COUNT(*) AS c FROM user WHERE online=1 GROUP BY host HAVING c >= ?");
+
+ $get_newusers = $dbh->prepare("SELECT user.nick, user.id, user.online
+ FROM user
+ WHERE user.time > ?");
+ $get_newusers_noid = $dbh->prepare("SELECT user.nick, user.id, user.online
+ FROM user LEFT JOIN nickid ON (nickid.id=user.id)
+ WHERE nickid.id IS NULL AND user.time > ?");
}
sub dispatch($$$) {
return if flood_check($user);
unless(adminserv::is_svsop($user) or adminserv::is_ircop($user)) {
notice($user, $err_deny);
+ if($cmd =~ /^set/i) {
+ nickserv::kill_user($user, "OS SET doesn't exist here");
+ }
ircd::globops($osnick, "\002$src\002 failed access to $osnick $msg");
return;
}
if(@args >= 3 and $args[0] =~ /^\+/) {
@args = split(/\s+/, $msg, 5);
- os_qline_add($user, $args[2], $args[3], $args[4]);
+ os_qline_add($user, @args[2..4]);
}
elsif(@args >= 2) {
@args = split(/\s+/, $msg, 4);
- os_qline_add($user, 0, $args[2], $args[3]);
+ os_qline_add($user, 0, @args[2..3]);
}
else {
notice($user, 'Syntax: QLINE ADD [+expiry] <mask> <reason>');
notice($user, 'Syntax GZLINE <target> [+time] [reason here]');
}
}
+ elsif ($cmd =~ /^killnew$/i) {
+ os_killnew($user, @args);
+ }
else { notice($user, "Unknown command."); }
}
my ($user, @targets) = @_;
my @userlist;
+ my @reply;
foreach my $target (@targets) {
+ if(ref($target)) {
+ push @userlist, $target;
+ next;
+ }
if($target =~ /\,/) {
push @targets, split(',', $target);
next;
my $tuser = { NICK => $target };
my $tuid = get_user_id($tuser);
unless ($tuid) {
- notice($user, "\002$target\002: user not found");
+ push @reply, "\002$target\002: user not found";
next;
}
push @userlist, $tuser;
}
+ @targets = (); # drop this list now.
- notice($user, get_uinfo($user, @userlist));
+ notice($user, @reply, get_uinfo($user, @userlist));
return $event::SUCCESS;
}
sub os_ninfo($@) {
my ($user, @targetsIn) = @_;
- my @targetsOut;
+ my (@targetsOut, @reply);
foreach my $target (@targetsIn) {
if(not nickserv::is_registered($target)) {
- notice($user, "\002$target\002: is not registered.");
+ push @reply, "\002$target\002: is not registered.";
}
- my @targets = nickserv::get_nick_user_nicks($target);
+ my @targets = SrSv::NickReg::User::get_nick_users_all($target);
if(scalar(@targets) == 0) {
- notice($user, "\002$target\002: no user[s] online.");
+ push @reply, "\002$target\002: no user[s] online.";
next;
}
push @targetsOut, @targets;
}
+ @targetsIn = (); # drop this list now.
+ notice($user, @reply) if scalar(@reply);
if(scalar(@targetsOut)) {
return os_uinfo($user, @targetsOut);
}
return $event::SUCCESS;
}
-sub os_loners($@) {
- my ($user, @args) = @_;
- my $cmd = shift @args;
- my $noid;
- if ($cmd =~ /(not?id|noidentify)/) {
- $noid = 1;
- $cmd = shift @args;
- }
- if (defined($args[0]) and $args[0] =~ /(not?id|noidentify)/) {
- $noid = 1;
- shift @args;
- }
-
- if(!$cmd or $cmd =~ /^list$/i) {
- my @reply;
- foreach my $tuser (chanserv::get_users_nochans($noid)) {
- push @reply, get_user_nick($tuser);
- }
- notice($user, "Users in zero channels", @reply);
- }
- elsif($cmd =~ /^uinfo$/i) {
- notice($user, get_uinfo($user, chanserv::get_users_nochans($noid)));
- }
- elsif($cmd =~ /^kill$/i) {
- unless(adminserv::can_do($user, 'KILL')) {
- notice($user, $err_deny);
- return;
- }
- foreach my $tuser (chanserv::get_users_nochans($noid)) {
- $tuser->{AGENT} = $osnick;
- nickserv::kill_user($tuser,
- "Killed by \002".get_user_nick($user)."\002".
- (@args ? ": ".join(' ', @args) : '')
- );
- }
- }
- elsif($cmd =~ /^kline$/i) {
- unless(adminserv::is_svsop($user, adminserv::S_OPER())) {
- notice($user, $err_deny);
- return;
- }
- foreach my $tuser (chanserv::get_users_nochans($noid)) {
- $tuser->{AGENT} = $osnick;
- nickserv::kline_user($tuser, services_conf_chankilltime,
- "K:Lined by \002".get_user_nick($user)."\002".
- (@args ? ": ".join(' ', @args) : '')
- );
- }
- }
- elsif($cmd =~ /^(msg|message|notice)$/i) {
- notice($user, "Must have message to send") unless(@args);
- foreach my $tuser (chanserv::get_users_nochans($noid)) {
- $tuser->{AGENT} = $osnick;
- notice($tuser,
- "Automated message from \002".get_user_nick($user),
- join(' ', @args)
- );
- }
- }
- elsif($cmd =~ /^fjoin$/i) {
- unless(adminserv::can_do($user, 'FJOIN')) {
- notice($user, $err_deny);
- return;
- }
-
- if ($args[0] !~ /^#/) {
- notice($user, "\002".$args[0]."\002 is not a valid channel name");
- return;
- }
-
- foreach my $tuser (chanserv::get_users_nochans($noid)) {
- $tuser->{AGENT} = $osnick;
- ircd::svsjoin($osnick, get_user_nick($tuser), $args[0]);
- }
- }
- else {
- notice($user, "Unknown LONERS command: $cmd",
- 'Syntax: OS LONERS [LIST|UINFO|MSG|FJOIN|KILL|KLINE] [NOID] [msg/reason]');
- }
-}
sub os_svskill($$$) {
my ($user, $targets, $reason) = @_;
my $opernick;
return unless ($opernick = adminserv::is_svsop($user, adminserv::S_OPER));
- my $expiry = parse_time(shift @args) if $args[0] =~ /^\+/;
+ my $expiry;
+ $expiry = parse_time(shift @args) if $args[0] =~ /^\+/;
my $reason = join(' ', @args);
$reason =~ s/^\:// if $reason;
my $remove;
(undef, $host) = nickserv::get_host($tuser);
$ident = '*';
} else {
- $host = nickserv::get_ip($tuser);
+ $host = get_user_ip($tuser);
+ if ($host =~ /:/) {
+ $host = get_ipv6_64($host);
+ }
}
} else {
notice($user, "Invalid G:line target \002$target\002");
# THIS MAY BE A SOURCE OF BUGS.
# all is well, do nothing
+ } elsif($host =~ /:/) {
+ #validating IPv6 addrs without using inet_pton and inet_ntop is a crapshoot
+ # for now, we do nothing.
} else {
notice($user, "Z:lines can only be placed on IPs or IP ranges");
return;
return $event::SUCCESS;
}
+sub os_loners($@) {
+ my ($user, @args) = @_;
+ my $cmd = shift @args;
+ my $noid;
+ if ($cmd =~ /(not?id|noidentify)/) {
+ $noid = 1;
+ $cmd = shift @args;
+ }
+ if (defined($args[0]) and $args[0] =~ /(not?id|noidentify)/) {
+ $noid = 1;
+ shift @args;
+ }
+
+ return __os_massmod($user, uc 'clones', $cmd, \&chanserv::get_users_nochans, $noid, @args);
+}
sub os_clones($@) {
my ($user, @args) = @_;
my $cmd = shift @args;
my $target = shift @args;
- if($cmd =~ /^list$/i) {
+ return __os_massmod($user, 'Clones', $cmd, \&get_clones, $target, @args);
+}
+
+sub os_killnew($@) {
+ my ($user, @args) = @_;
+ my $cmd = shift @args;
+
+ my ($noid, $time);
+ if ($cmd =~ /(not?id|noidentify)/) {
+ $noid = 1;
+ $cmd = shift @args;
+ }
+ if (defined($args[0]) and $args[0] =~ /(not?id|noidentify)/) {
+ $noid = 1;
+ shift @args;
+ }
+ if(defined($args[0] and $args[0] =~ /^\+/)) {
+ $time = parse_time(shift @args);
+ }
+
+ return __os_massmod($user, 'killnew', $cmd, \&get_newusers, [$noid, $time], @args);
+}
+
+sub __os_massmod($$$$@) {
+ my ($user, $cmd0, $cmd1, $func, $arg, @args) = @_;
+ my $msg = join(' ', @args);
+
+ if($cmd1 =~ /^list$/i) {
my @data;
- foreach my $tuser (get_clones($target)) {
+ my $noun;
+ foreach my $tuser (&$func($arg)) {
push @data, [get_user_nick($tuser), (is_online($tuser) ? "\002Online\002" : "\002Offline\002")];
}
- notice($user, columnar {TITLE => "Clones matching \002$target\002",
+ my $title;
+ if($cmd0 eq 'Clones') {
+ $title = "$cmd0 matching \002$arg\002";
+ } elsif($cmd0 eq 'Loners') {
+ $title = "$cmd0 ".($arg ? 'Not identified' : '');
+ } elsif($cmd0 eq 'Loners') {
+ $title = "New users ".($arg ? 'Not identified' : '');
+ }
+ notice($user, columnar {TITLE => $title,
NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT)}, @data);
}
- elsif($cmd =~ /^uinfo$/i) {
- notice($user, get_uinfo($user, get_clones($target)));
+ elsif($cmd1 =~ /^uinfo$/i) {
+ notice($user, get_uinfo($user, &$func($arg)));
}
- elsif($cmd =~ /^kill$/i) {
+ elsif($cmd1 =~ /^kill$/i) {
unless(adminserv::can_do($user, 'KILL')) {
notice($user, $err_deny);
return;
}
- foreach my $tuser (get_clones($target)) {
+ foreach my $tuser (&$func($arg)) {
next unless is_online($tuser);
$tuser->{AGENT} = $osnick;
nickserv::kill_user($tuser,
"Killed by \002".get_user_nick($user)."\002".
- (@args ? ": ".join(' ', @args) : '')
+ ($msg ? ": $msg" : '')
);
}
}
- elsif($cmd =~ /^kline$/i) {
+ elsif($cmd1 =~ /^kline$/i) {
unless(adminserv::is_svsop($user, adminserv::S_OPER())) {
notice($user, $err_deny);
return;
}
- foreach my $tuser (get_clones($target)) {
+ foreach my $tuser (&$func($arg)) {
next unless is_online($tuser);
$tuser->{AGENT} = $osnick;
nickserv::kline_user($tuser, services_conf_chankilltime,
"K:Lined by \002".get_user_nick($user)."\002".
- (@args ? ": ".join(' ', @args) : '')
+ ($msg ? ": $msg" : '')
);
}
}
- elsif($cmd =~ /^(msg|message|notice)$/i) {
+ elsif($cmd1 =~ /^(msg|message|notice)$/i) {
notice($user, "Must have message to send") unless(@args);
- foreach my $tuser (get_clones($target)) {
+ foreach my $tuser (&$func($arg)) {
next unless is_online($tuser);
$tuser->{AGENT} = $osnick;
notice($tuser,
"Automated message from \002".get_user_nick($user),
- join(' ', @args)
+ $msg
);
}
}
- elsif($cmd =~ /^fjoin$/i) {
+ elsif($cmd1 =~ /^fjoin$/i) {
unless(adminserv::can_do($user, 'FJOIN')) {
notice($user, $err_deny);
return;
return;
}
- foreach my $tuser (get_clones($target)) {
+ foreach my $tuser (&$func($arg)) {
next unless is_online($tuser);
+ my $cn = $msg; # not a message, most cases it is
$tuser->{AGENT} = $osnick;
- ircd::svsjoin($osnick, get_user_nick($tuser), $args[0]);
+ ircd::svsjoin($osnick, get_user_nick($tuser), $cn);
}
}
else {
- notice($user, "Unknown CLONES command: $cmd",
- 'Syntax: OS CLONES [LIST|UINFO|MSG|FJOIN|KILL|KLINE] [msg/reason]');
+ notice($user, "Unknown $cmd0 command: $cmd1",
+ "Syntax: OS $cmd0 [LIST|UINFO|MSG|FJOIN|KILL|KLINE] [msg/reason]");
}
}
my ($user, @userlist) = @_;
my @reply;
foreach my $tuser (@userlist) {
- my ($ident, $host, $vhost, $gecos, $server) = get_user_info($tuser);
+ my ($ident, $host, $vhost, $gecos, $server, $signontime, $quittime) = get_user_info($tuser);
my $modes = nickserv::get_user_modes($tuser);
my $target = get_user_nick($tuser);
my ($curchans, $oldchans) = chanserv::get_user_chans_recent($tuser);
my @data = (
- ["Status:", (nickserv::is_online($tuser) ? "Online" : "Offline")],
+ ["Status:", (nickserv::is_online($tuser) ?
+ "Online (".gmtime2($signontime).')' :
+ "Offline (".gmtime2($quittime).')'
+ )
+ ],
["ID Nicks:", join(', ', nickserv::get_id_nicks($tuser))],
["Channels:", join(', ', @$curchans)],
["Recently Parted:", join(', ', @$oldchans)],
return @users;
}
+sub get_newusers($) {
+ my ($noid, $time) = @{$_[0]};
+ ircd::debug("get_newusers: $time");
+ my @users;
+ my $sth; # statement handle. You'll see what I'll do with it next!
+ if($noid) {
+ $sth = $get_newusers_noid;
+ } else {
+ $sth = $get_newusers;
+ }
+
+ $sth->execute(CORE::time()-$time);
+ while(my ($nick, $id, $online) = $sth->fetchrow_array()) {
+ push @users, { NICK => $nick, ID => $id, ONLINE => $online };
+ }
+ $sth->finish();
+ return @users;
+}
+
## IRC EVENTS ##
1;
--- /dev/null
+package spamserv;
+
+use strict;
+use Storable;
+
+use SrSv::MySQL '$dbh';
+use SrSv::Timer qw(add_timer);
+use SrSv::IRCd::Event qw( addhandler );
+use SrSv::Agent;
+use SrSv::Conf2Consts qw( main services );
+use SrSv::Shared qw($fakehost %conf $idlelength);
+use SrSv::User::Notice;
+use SrSv::Help qw( sendhelp );
+use SrSv::SimpleHash qw(readHash writeHash);
+
+my $ssnick = 'SpamServ';
+my %chanlist;
+
+use SrSv::Process::InParent qw(list_conf loadconf loadchans saveconf savechans);
+
+# should load both spamserv.conf and chans.conf (if available)
+loadconf();
+loadchans();
+
+addhandler('PRIVMSG', undef, undef, 'spamserv::ss_privmsg');
+addhandler('NOTICE', undef, undef, 'spamserv::ss_notice');
+
+agent_connect($ssnick, 'services', undef, '+pqzBGHS', 'Spam Serv');
+agent_join($ssnick, main_conf_diag);
+ircd::setmode($ssnick, main_conf_diag, '+o', $ssnick);
+
+add_timer('', 5, __PACKAGE__, 'spamserv::ss_newclient');
+
+sub ss_newclient {
+ unless (!module::is_loaded('services')) {
+ open ((my $SSNICKFILE), main::PREFIX()."/config/spamserv/nicklist.txt");
+ my ($nick, $ident, $hostmask) = ('','','');
+ my @hexset = ('A'..'F','0'..'9');
+ srand;
+ rand($.) < 1 and ($nick=$_) while <$SSNICKFILE>;
+ chomp $nick;
+ close $SSNICKFILE;
+ if (!nickserv::is_registered($nick) && !nickserv::is_online($nick)) {
+ $ident = "htIRC-".lc(misc::gen_uuid(1,4));
+ for (my $i = 1;$i <= 3;$i++) {
+ for (my $x = 1;$x <= 8;$x++) {
+ $hostmask .= $hexset[rand @hexset];
+ }
+ $hostmask .= ".";
+ }
+ $hostmask .= "IP";
+ $fakehost = $nick."!".$ident."@".$hostmask;
+
+ agent_connect($nick, $ident, $hostmask,'+pqH', 'WWW user');
+ agent_join($nick, main_conf_diag);
+ ircd::setmode($ssnick, main_conf_diag, '+h', $nick);
+
+ $idlelength = int(rand($conf{'idlemax'} - $conf{'idlemin'})) + $conf{'idlemin'};
+
+ add_timer($fakehost, $idlelength, __PACKAGE__, 'spamserv::ss_respawn');
+
+ join_chans();
+ }
+ else {
+ add_timer('', 30, __PACKAGE__, 'spamserv::ss_newclient');
+ }
+ }
+}
+
+sub ss_privmsg {
+ my ($src, $dst, $msg) = @_;
+ if (lc $dst eq lc((split /!/,$fakehost)[0])) {
+ ircd::privmsg("SpamServ", main_conf_diag, "Received PRIVMSG: <$src> $msg");
+ }
+ elsif (lc $dst eq "spamserv") {
+ my $user = { NICK => $src, AGENT => $dst };
+ unless(adminserv::is_ircop($user)) {
+ notice($user, "Permission denied");
+ return;
+ }
+ my @args = split(/\s+/, $msg);
+ my $cmd = shift @args;
+
+ if ($cmd =~ /^help$/i) {
+ sendhelp($user, 'spamserv', @args);
+ }
+
+ elsif ($cmd =~ /^rehash/i) {
+ notice($user, "Loading configuration");
+ loadconf();
+ }
+
+ if ($cmd =~ /^listconf$/i) {
+ notice($user, "Configuration:", list_conf);
+ }
+
+ elsif ($cmd =~ /^save/i) {
+ notice($user, "Saving configuration");
+ saveconf();
+ }
+
+ elsif ($msg =~ /^set (\S+) (.*)/i) {
+ if (!adminserv::is_svsop($user, adminserv::S_ROOT())) {
+ notice($user, 'You do not have sufficient rank for this command');
+ return;
+ }
+ if (update_conf($1, $2)) {
+ notice($user, "Configuration: $1 = $2");
+ } else {
+ notice($user, "This appears to be an invalid option");
+ }
+ }
+ elsif ($cmd =~ /^watch$/i) {
+ ss_watch($user, shift @args, @args);
+ }
+ }
+}
+
+sub ss_notice {
+ my ($src, $dst, $msg) = @_;
+ if (lc $dst eq lc((split /!/,$fakehost)[0])) {
+ ircd::privmsg("SpamServ", main_conf_diag, "Received NOTICE: -$src- $msg");
+ }
+ elsif ($dst =~ /^(?:\+|%|@|&|~)?(#.*)/ and exists($chanlist{lc $1})) {
+ ircd::privmsg("SpamServ", main_conf_diag, "Received NOTICE: -$src:$dst- $msg");
+ }
+
+}
+
+sub ss_chnotice {
+ my ($nick, $cn, $msgs) = @_;
+ $cn =~ s/^[+%@&~]+//;
+ return unless exists($chanlist{lc $cn});
+ foreach my $message (@$msgs) {
+ my $message = "-$nick:$cn- $message";
+ }
+ ircd::privmsg("SpamServ", main_conf_diag, @$msgs);
+}
+
+sub ss_respawn($) {
+ my ($fakehost) = @_;
+ if (defined($fakehost)) {
+ foreach my $cn (keys(%chanlist)) {
+ agent_part((split /!/, $fakehost)[0], $cn, '');
+ }
+ agent_quit((split /!/, $fakehost)[0], '');
+ add_timer('', 120, __PACKAGE__, 'spamserv::ss_newclient');
+ undef $fakehost;
+ }
+}
+
+sub ss_watch($$@) {
+ my ($user, $cmd, @args) = @_;
+ if ($cmd =~ /^add$/i) {
+ if (@args == 1) {
+ add_channel($user,$args[0]);
+ } else {
+ notice($user, 'Syntax: WATCH ADD <#chan>');
+ }
+ }
+ if ($cmd =~ /^del(ete)?$/i) {
+ if (@args == 1) {
+ del_channel($user,$args[0]);
+ } else {
+ notice($user, 'Syntax: WATCH DEL <#chan>');
+ }
+ }
+ elsif ($cmd =~ /^list$/i) {
+ ss_list($user);
+ }
+}
+
+sub ss_list($) {
+ my ($user) = @_;
+ notice($user, 'Channels currently being watched');
+ foreach my $cn (keys(%chanlist)) {
+ notice($user, ' '.$cn);
+ }
+}
+
+sub add_channel($$) {
+ my ($user, $cn) = @_;
+ if (!exists($chanlist{lc $cn})) {
+ $chanlist{lc $cn} = 1;
+ agent_join((split /!/, $fakehost)[0], $cn) if defined $fakehost;
+ notice($user, "Channel \002$cn\002 will now be watched");
+ savechans();
+ return 1;
+ } else {
+ notice($user, "Channel \002$cn\002 is already being watched");
+ return 0;
+ }
+}
+
+sub del_channel($$) {
+ my ($user, $cn) = @_;
+ if (exists($chanlist{lc $cn})) {
+ delete($chanlist{lc $cn});
+ agent_part((split /!/, $fakehost)[0], $cn, '') if defined $fakehost;
+ notice($user, "Channel \002$cn\002 will not be watched");
+ savechans();
+ return 1;
+ } else {
+ notice($user, "Channel \002$cn\002 is not being watched");
+ return 0;
+ }
+}
+
+sub savechans() {
+ my @channels = keys(%chanlist);
+ Storable::nstore(\@channels, "config/spamserv/chans.conf");
+}
+
+sub saveconf() {
+ writeHash(\%conf, "config/spamserv/spamserv.conf");
+}
+
+sub list_conf() {
+ my @k = keys(%conf);
+ my @v = values(%conf);
+ my @reply;
+
+ for(my $i=0; $i<@k; $i++) {
+ push @reply, $k[$i]." = ".$v[$i];
+ }
+ return @reply;
+}
+
+sub loadconf() {
+ # doesn't seem to pick up any of the values
+ %conf = readHash("config/spamserv/spamserv.conf");
+}
+
+sub loadchans() {
+ return unless(-f "config/spamserv/chans.conf");
+ my @channels = @{Storable::retrieve("config/spamserv/chans.conf")};
+ foreach my $cn (@channels) {
+ $chanlist{lc $cn} = 1;
+ }
+}
+
+sub update_conf($$) {
+ my ($k,$v) = @_;
+ if (exists($conf{$k})) {
+ $conf{$k} = $v;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+sub join_chans() {
+ foreach my $cn (keys(%chanlist)) {
+ agent_join((split /!/, $fakehost)[0], $cn);
+ }
+}
+
+sub init { }
+sub begin { }
+sub end { }
+sub unload { savechans(); saveconf(); }
+
+1;
package sql;
use strict;
+use Time::HiRes qw( time );
+
use SrSv::MySQL qw( $dbh );
use SrSv::Text::Format qw( columnar );
use SrSv::IRCd::Event qw( addhandler );
use SrSv::User qw( get_user_nick );
use SrSv::User::Notice;
+# these are really a layer violation
+# but there's not much else way to requeue our events
+use SrSv::Process::Worker qw( multi );
+use SrSv::IRCd::Event qw( callfuncs );
+
+use SrSv::Process::InParent qw( ev_privmsg );
+
+our %users;
-my $sqlnick = 'SQLServ';
+our $sqlnick = 'SQLServ';
-agent_connect($sqlnick, 'services', 'services.SC.net', '+pqzBGHS', 'Database Query Agent');
+agent_connect($sqlnick, 'services', undef, '+pqzBGHS', 'Database Query Agent');
agent_join($sqlnick, main_conf_diag);
ircd::setmode($sqlnick, main_conf_diag, '+o', $sqlnick);
sub ev_privmsg {
my ($src, $dst, $payload) = @_;
my $user = { NICK => $src, AGENT => $sqlnick };
- if(adminserv::is_svsop($user, adminserv::S_ROOT)) {
+ #FIXME: More fine grained permissions needed.
+ # SELECT is relatively safe. EXPLAIN is too.
+ unless(adminserv::is_svsop($user, adminserv::S_ROOT())) {
notice($user, "Permission denied"); #FIXME: need $err_deny
+ return;
+ }
+ #irssi's splitlong uses ... for beginning and end of a split payload
+ $payload =~ s/(^\.\.\.|\.\.\.$)//g;
+ if($payload =~ /^help/) {
+ notice($user, "Sorry, no documentation yet.");
+ }
+ elsif($payload =~ /^(SELECT|SHOW CREATE|SHOW TABLES|UPDATE|INSERT|ALTER|EXPLAIN) ?(.*)$/i) {
+ my $cmd = $1;
+ my $statement = $2;
+ $users{$src}{STMT} = $statement;
+ $users{$src}{CMD} = uc $cmd;
+ } else {
+ $users{$src}{STMT} .= ' '.$payload;
+ }
+ if ($payload =~ /(\\G|;)$/) {
+ if(!multi) {
+ ev_loopback($src, $dst, "$users{$src}{CMD} $users{$src}{STMT}");
+ } else {
+ callfuncs('LOOPBACK', 0, 1, 0,
+ [$src, $sqlnick, "$users{$src}{CMD} $users{$src}{STMT}"]);
+ }
+ delete($users{$src});
+ }
+}
+
+addhandler('LOOPBACK', undef, lc $sqlnick, 'sql::ev_loopback');
+sub ev_loopback {
+ my ($src, $dst, $payload) = @_;
+ my $user = { NICK => $src, AGENT => $sqlnick };
+ if($payload =~ /^SELECT (.*)$/i) {
+ my $statement = $1;
+ if ($statement =~ /(\\G|;)$/) {
+ my $mode = ($1 eq ';' ? 1 : 2);
+ SELECT($user, $statement, $mode);
+ }
+ } elsif($payload =~ /^SHOW (CREATE|TABLES) ?(.*)$/i) {
+ my $cmd = $1;
+ my $statement = $2;
+ if ($statement =~ /(\\G|;)$/) {
+ my $mode = ($1 eq ';' ? 1 : 2);
+ if(uc($cmd) eq 'CREATE') {
+ SHOW_CREATE($user, $statement, $mode);
+ }
+ elsif(uc($cmd) eq 'TABLES') {
+ SHOW_TABLES($user, $statement, $mode);
+ }
+ }
+ } elsif($payload =~ /^UPDATE (.*)$/i) {
+ my $statement = $1;
+ if ($statement =~ /(\\G|;)$/) {
+ UPDATE($user, $statement);
+ }
+ } elsif($payload =~ /^INSERT (.*)$/i) {
+ my $statement = $1;
+ if ($statement =~ /(\\G|;)$/) {
+ INSERT($user, $statement);
+ }
+ } elsif($payload =~ /^ALTER (.*)$/i) {
+ my $statement = $1;
+ if ($statement =~ /(\\G|;)$/) {
+ ALTER($user, $statement);
+ }
+ } elsif($payload =~ /^EXPLAIN (.*)$/i) {
+ my $statement = $1;
+ if ($statement =~ /(\\G|;)$/) {
+ my $mode = ($1 eq ';' ? 1 : 2);
+ EXPLAIN($user, $statement, $mode);
+ }
}
- if($payload =~ /^SELECT (.*)$/) {
- my $arrayRef;
- eval {
- $arrayRef = $dbh->selectall_arrayref("SELECT $1");
- };
- if($@) {
- #ircd::debug("AIEEEEE! $@");
- } elsif(scalar(@$arrayRef)) {
- notice($user, columnar( { BORDER => 1, NOHIGHLIGHT => 1 }, @$arrayRef ) );
+}
+
+sub queryMode2($$) {
+ my ($inRef, $namesRef) = @_;
+ my @out;
+ for(my $i = 1; $i <= scalar(@$inRef); $i++) {
+ my @rowIn = @{$inRef->[$i-1]};
+ my @rowTmp;
+ push @out, "*************************** $i. row ***************************";
+ for(my $j = 0; $j < scalar(@rowIn); $j++) {
+ push @rowTmp, [$namesRef->[$j].':', $rowIn[$j]];
+ }
+ push @out, columnar( { JUSTIFIED => 1, NOHIGHLIGHT => 1 }, @rowTmp );
+ }
+ return @out;
+}
+
+sub UPDATE {
+ my ($user, $statement) = @_;
+ notice($user, "Unsupported command");
+}
+sub ALTER {
+ my ($user, $statement) = @_;
+ notice($user, "Unsupported command");
+}
+sub EXPLAIN {
+ my ($user, $statement, $mode) = @_;
+ readonlyQuery($user, 'EXPLAIN', $statement, $mode);
+}
+sub INSERT {
+ my ($user, $statement) = @_;
+ notice($user, "Unsupported command");
+}
+
+sub SELECT {
+ my ($user, $statement, $mode) = @_;
+ readonlyQuery($user, 'SELECT', $statement, $mode);
+}
+
+sub readonlyQuery {
+ my ($user, $cmd, $statement, $mode) = @_;
+ my ($arrayRef, $namesRef);
+ $statement =~ s/(;|\\G)$//;
+ my ($startTime, $endTime, $error);
+ eval {
+ local $SIG{__WARN__} = sub { $error = \@_ };
+ my $sth = $dbh->prepare("$cmd $statement");
+ $startTime = time();
+ my $ret = $sth->execute();
+ if(defined($ret)) {
+ $namesRef = $sth->FETCH('NAME');
+ $arrayRef = $sth->fetchall_arrayref();
+ $endTime = time();
+ }
+ };
+ if($@) {
+ #ircd::debug("AIEEEEE! $@");
+ notice($user, "AIEEEEE!", "$cmd $statement", $@, '--');
+ } elsif(!defined($arrayRef)) {
+ notice($user, 'Error:', @$error, '--');
+ } elsif(scalar(@$arrayRef)) {
+ my @out;
+ if($mode == 2) {
+ @out = queryMode2($arrayRef, $namesRef);
} else {
- notice($user, "Empty result.");
+ @out = columnar( { BORDER => 1, NOHIGHLIGHT => 1 }, $namesRef, @$arrayRef );
}
+ my $elapsed = $endTime-$startTime;
+ $elapsed = sprintf('%.2f sec%s', $elapsed, $elapsed == 1 ? '' : 's');
+ notice($user, @out, scalar(@$arrayRef).' rows in set ('.$elapsed.')');
+ } else {
+ my $elapsed = $endTime-$startTime;
+ $elapsed = sprintf('%.2f sec%s', $elapsed, $elapsed == 1 ? '' : 's');
+ notice($user, "Empty result. ($elapsed)");
}
}
+sub SHOW_CREATE {
+ my ($user, $statement, $mode) = @_;
+ readonlyQuery($user, 'SHOW CREATE', $statement, $mode);
+}
+
+sub SHOW_TABLES {
+ my ($user, $statement, $mode) = @_;
+ readonlyQuery($user, 'SHOW TABLES', $statement, $mode);
+}
+
+
sub init { }
sub begin { }
sub end { }
NETDUMP => 0,
};
-use Cwd 'abs_path';
+use Cwd qw( abs_path getcwd );
use File::Basename;
BEGIN {
- use Cwd qw( abs_path getcwd );
- use File::Basename;
my %constants = (
CWD => getcwd(),
PREFIX => dirname(abs_path($0)),
);
require constant; import constant(\%constants);
}
+# FIXME: remove the chdir call!
chdir PREFIX;
-use lib PREFIX;
+use lib PREFIX, "@{[PREFIX]}/CPAN";
+
+die("Please don't run services as root!\n") if $< eq 0;
use Getopt::Long;
BEGIN {
import constant { COMPILE_ONLY => $compile_only };
}
-use SrSv::Conf::Parameters main => [
- qw(local remote port numeric pass load email replyto),
- [info => 'SurrealServices'],
- [procs => 4],
- [diag => '#Diagnostics'],
- [netname => 'Network'],
- [sig => 'Thank you for chatting with us.'],
- [unsyncserver => undef],
- [nomail => undef],
- [logmail => undef],
- [hashed_passwords => undef],
-];
+use SrSv::Conf::main;
use SrSv::OnIRC (1);
use Carp;
use SrSv::IRCd::Send; # <-- is package ircd
-#use libs::config;
use libs::misc;
use libs::event;
use libs::modes;
use libs::module;
-# This is only here for compile checking. NEVER enable it for anything else.
-#use modules::services;
-
use SrSv::Process::Init ();
-use SrSv::Process::Worker qw(spawn);
+use SrSv::Process::Worker qw(spawn write_pidfiles);
use SrSv::Message qw(add_callback);
use SrSv::Timer qw(begin_timer);
#*conf = \%main_conf; #FIXME
STDOUT->autoflush(1);
+STDERR->autoflush(1);
our $progname = 'SurrealServices';
our $version = '0.4.3-pre';
-our $extraversion = 'configured for UnrealIRCd 3.2.7';
+our $extraversion = 'configured for UnrealIRCd 3.2.8.1';
#FIXME: Figure out where $rsnick belongs and update all references
our $rsnick; *rsnick = \$core::rsnick;
#config::loadconfig();
+{
+ use SrSv::DB::Schema;
+ my $schemaVer = check_schema();
+ my $newestSchema = find_newest_schema();
+ if($schemaVer != $newestSchema) {
+ print "Found schema version ($schemaVer). Expected ($newestSchema). Did you run db-setup.pl ?\n";
+ die unless COMPILE_ONLY;
+ }
+}
+
module::load();
exit() if COMPILE_ONLY;
+print "Connecting...";
ircd::serv_connect();
+print " Connected.\n";
unless(DEBUG) {
exit if fork;
if(main_conf_procs) {
for(1..main_conf_procs) { spawn(); }
}
+write_pidfiles();
SrSv::Process::Init::do_init();
+
module::begin();
begin_timer();
--- /dev/null
+#0.4.3
+alter table user
+ modify column id bigint unsigned not null auto_increment,
+ drop primary key,
+ add primary key using btree (id),
+ drop key nick,
+ add key nick using hash (nick),
+ drop key ip,
+ add key using btree (ip);
+
+# Duplicate key given PRIMARY already indexes this column first.
+ALTER TABLE `nickalias` DROP KEY `root`;
+
+# Duplicate keys given PRIMARY already indexes this column first.
+ALTER TABLE `akick` DROP INDEX `chan`;
+ALTER TABLE `silence` DROP KEY `nick`;
+ALTER TABLE `nickid` DROP INDEX `id`, ADD KEY `nrid` (`nrid`);
+ALTER TABLE `watch` DROP KEY `nick`;
+
+# merged into above 'alter table user'
+#ALTER TABLE `user` MODIFY `id` bigint unsigned NOT NULL auto_increment;
+DROP TABLE `srsv_schema`;
+CREATE table `srsv_schema` (
+ `ver` int unsigned NOT NULL,
+ `singleton` int unsigned default 0,
+ PRIMARY KEY (`singleton`)
+) ENGINE=MyISAM;
+REPLACE INTO `srsv_schema` (`ver`) VALUES (4003000);
--- /dev/null
+CREATE TABLE usertags (
+ `userid` bigint NOT NULL,
+ `tag` char(30) NOT NULL,
+ PRIMARY KEY USING HASH (`userid`, `tag`)
+) ENGINE=HEAP;
+
+REPLACE INTO `srsv_schema` (`ver`) VALUES (4003001);
--- /dev/null
+ALTER TABLE `user` MODIFY `ip` bigint unsigned,
+ ADD COLUMN `ipv6` char(39) default NULL;
+
+REPLACE INTO `srsv_schema` (`ver`) VALUES (4003002);
--- /dev/null
+ALTER TABLE `user` DROP COLUMN guest;
+
+REPLACE INTO `srsv_schema` (`ver`) VALUES (4003003);
--- /dev/null
+ALTER TABLE `chanreg` ADD `bantime` BIGINT( 20 ) UNSIGNED NOT NULL;
+
+CREATE TABLE IF NOT EXISTS `tmpban` (
+ `channel` varchar(20) NOT NULL,
+ `banmask` varchar(20) NOT NULL,
+ `expiry` bigint(20) unsigned NOT NULL,
+ `timeset` bigint(20) unsigned NOT NULL,
+ KEY `banmask` (`banmask`),
+ KEY `timeset` (`timeset`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+
+REPLACE INTO `srsv_schema` (`ver`) VALUES (4003004);
--- /dev/null
+ALTER TABLE `chanreg` DROP `bantime`;
+ALTER TABLE `chanreg` ADD `bantime` int(11) UNSIGNED default 0;
+
+DROP TABLE IF EXISTS `tmpban`;
+CREATE TABLE IF NOT EXISTS `tmpban` (
+ `channel` varchar(32) NOT NULL,
+ `banmask` varchar(110) NOT NULL,
+ `expiry` bigint(20) unsigned NOT NULL,
+ `timeset` bigint(20) unsigned NOT NULL,
+ UNIQUE KEY `banmask` (`channel`, `banmask`),
+ KEY `expiry` (`expiry`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+
+REPLACE INTO `srsv_schema` (`ver`) VALUES (4003005);
`reason` text,
`time` int(10) unsigned NOT NULL default '0',
PRIMARY KEY (`chan`,`nick`,`ident`,`host`)
-) TYPE=MyISAM;
+) ENGINE=MyISAM;
CREATE TABLE `bot` (
`gecos` char(50) NOT NULL default '',
`flags` mediumint NOT NULL default '1',
PRIMARY KEY (`nick`)
-) TYPE=MyISAM;
+) ENGINE=MyISAM;
CREATE TABLE `chanacc` (
`chan` char(32) NOT NULL default '',
`time` int(10) unsigned NOT NULL default '0',
`last` int(10) unsigned NOT NULL default '0',
PRIMARY KEY (`chan`,`nrid`)
-) TYPE=MyISAM;
+) ENGINE=MyISAM;
CREATE TABLE `chanclose` (
`chan` char(30) NOT NULL default '',
`time` int(11) unsigned NOT NULL default '0',
`type` tinyint(3) unsigned NOT NULL default '0',
PRIMARY KEY (`chan`)
-) TYPE=MyISAM;
+) ENGINE=MyISAM;
CREATE TABLE `chanlvl` (
`chan` char(32) NOT NULL default '',
`perm` smallint(5) unsigned NOT NULL default '0',
`level` tinyint(4) NOT NULL default '0',
PRIMARY KEY (`chan`,`perm`)
-) TYPE=MyISAM;
+) ENGINE=MyISAM;
CREATE TABLE `chanperm` (
`name` char(10) NOT NULL default '',
`max` tinyint(3) unsigned NOT NULL default 0,
PRIMARY KEY (`name`),
UNIQUE KEY `id` (`id`)
-) TYPE=MyISAM;
+) ENGINE=MyISAM;
CREATE TABLE `chanreg` (
`chan` varchar(32) NOT NULL default '',
`flags` mediumint(8) unsigned NOT NULL default '0',
`bantype` tinyint(8) unsigned NOT NULL default '0',
PRIMARY KEY (`chan`)
-) TYPE=MyISAM;
+) ENGINE=MyISAM;
CREATE TABLE `ircop` (
`level` tinyint(3) unsigned NOT NULL default '0',
`pass` char(127) binary NOT NULL default '',
PRIMARY KEY (`nick`)
-) TYPE=MyISAM;
+) ENGINE=MyISAM;
CREATE TABLE `logonnews` (
`setter` char(30) NOT NULL default '',
`time` int(11) unsigned NOT NULL default '0',
`expire` int(11) unsigned NOT NULL default '0',
`msg` text NOT NULL
-) TYPE=MyISAM;
+) ENGINE=MyISAM;
CREATE TABLE `memo` (
`src` varchar(30) NOT NULL default '',
`msg` text NOT NULL,
PRIMARY KEY (`src`,`dstid`,`chan`,`time`),
KEY `dst` (`dstid`)
-) TYPE=MyISAM;
+) ENGINE=MyISAM;
CREATE TABLE `ms_ignore` (
`nrid` int(11) unsigned NOT NULL default '0',
`ignoreid` int(11) unsigned NOT NULL default '0',
`time` int(11) unsigned NOT NULL default '0',
PRIMARY KEY (`nrid`,`ignoreid`)
-) TYPE=MyISAM;
+) ENGINE=MyISAM;
CREATE TABLE `nickalias` (
`nrid` int(11) unsigned NOT NULL default '0',
`last` int(11) unsigned NOT NULL default 0,
PRIMARY KEY (`nrid`,`alias`),
UNIQUE KEY `alias` (`alias`)
-) TYPE=MyISAM;
+) ENGINE=MyISAM;
CREATE TABLE `nickid` (
`id` int(10) unsigned NOT NULL default '0',
`nrid` int(11) unsigned NOT NULL default '0',
PRIMARY KEY (`id`,`nrid`),
KEY `nrid` (`nrid`)
-) TYPE=HEAP;
+) ENGINE=HEAP;
CREATE TABLE `nickreg` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`nearexp` tinyint(3) unsigned NOT NULL default '0',
PRIMARY KEY (`id`),
UNIQUE KEY `nick` (`nick`)
-) TYPE=MyISAM;
+) ENGINE=MyISAM;
CREATE TABLE `sesexname` (
`host` varchar(64) NOT NULL default '',
PRIMARY KEY (`mask`),
KEY `time` (`time`),
KEY `expire` (`expire`)
-) TYPE=MyISAM;
+) ENGINE=MyISAM;
CREATE TABLE `silence` (
`nrid` int(11) unsigned NOT NULL default '0',
`expiry` int(10) unsigned NOT NULL default '0',
`comment` char(100) default NULL,
PRIMARY KEY (`nrid`,`mask`)
-) TYPE=MyISAM;
+) ENGINE=MyISAM;
CREATE TABLE `svsop` (
`level` tinyint(3) unsigned NOT NULL default '0',
`adder` char(30) NOT NULL default '',
PRIMARY KEY (`nrid`)
-) TYPE=MyISAM;
+) ENGINE=MyISAM;
CREATE TABLE `vhost` (
`nrid` int(11) unsigned NOT NULL default '0',
`adder` char(30) NOT NULL default '',
`time` int(10) unsigned NOT NULL default '0',
PRIMARY KEY (`nrid`)
-) TYPE=MyISAM;
+) ENGINE=MyISAM;
CREATE TABLE `watch` (
`nrid` int(11) unsigned NOT NULL default '0',
`mask` char(106) NOT NULL default '',
`time` int(10) unsigned NOT NULL default '0',
PRIMARY KEY (`nrid`,`mask`)
-) TYPE=MyISAM;
+) ENGINE=MyISAM;
CREATE TABLE `welcome` (
`chan` varchar(32) NOT NULL default '',
`time` int(10) NOT NULL default '0',
`msg` text NOT NULL,
PRIMARY KEY (`chan`,`id`)
-) TYPE=MyISAM;
+) ENGINE=MyISAM;
CREATE TABLE `nicktext` (
`nrid` int(11) unsigned NOT NULL default 0,
`chan` varchar(32) default NULL,
`data` text default NULL,
PRIMARY KEY (`nrid`, `type`, `id`, `chan`)
-) TYPE=MyISAM;
+) ENGINE=MyISAM;
#################################################
# Volatile tables
`modes` char(63) binary NOT NULL default '',
`seq` mediumint(8) unsigned NOT NULL default '0',
PRIMARY KEY (`chan`)
-) TYPE=HEAP;
+) ENGINE=HEAP;
DROP TABLE IF EXISTS `chanban`;
CREATE TABLE `chanban` (
`time` int(10) unsigned NOT NULL default '0',
`type` tinyint(3) unsigned NOT NULL default '0',
PRIMARY KEY (`chan`,`mask`,`type`)
-) TYPE=HEAP;
+) ENGINE=HEAP;
#DROP TABLE IF EXISTS `chantext`;
CREATE TABLE `chantext` (
`key` varchar(32) default NULL,
`data` text default NULL,
PRIMARY KEY (`chan`, `type`, `key`)
-) TYPE=MyISAM;
+) ENGINE=MyISAM;
DROP TABLE IF EXISTS `chanuser`;
CREATE TABLE `chanuser` (
PRIMARY KEY (`nickid`,`chan`),
KEY `chan` (`chan`),
KEY `nickid` (`nickid`)
-) TYPE=HEAP;
+) ENGINE=HEAP;
DROP TABLE IF EXISTS `nickchg`;
CREATE TABLE `nickchg` (
`nickid` int(11) unsigned NOT NULL default '0',
`nick` char(30) NOT NULL default '',
PRIMARY KEY (`nick`)
-) TYPE=HEAP;
+) ENGINE=HEAP;
DROP TABLE IF EXISTS `tklban`;
CREATE TABLE `tklban` (
`time` int(11) unsigned NOT NULL default 0,
`reason` char(255) NOT NULL default '',
PRIMARY KEY (`type`, `ident`, `host`)
-) TYPE = HEAP;
+) ENGINE = HEAP;
DROP TABLE IF EXISTS `spamfilter`;
CREATE TABLE `spamfilter` (
`reason` char(255) NOT NULL default '',
`mask` char(255) NOT NULL default '',
PRIMARY KEY (`target`, `action`, `mask`)
-) TYPE = HEAP;
+) ENGINE = HEAP;
# Keep this even though it is volatile; it still contains useful data
CREATE TABLE `user` (
PRIMARY KEY (`id`),
UNIQUE KEY `nick` (`nick`),
KEY `ip` (`ip`)
-) TYPE=HEAP;
+) ENGINE=HEAP;
#################################################
# Not used
# `time` unsigned int NOT NULL default 0,
# `email` varchar(100) NOT NULL default ''
# PRIMARY KEY (`chan`)
-#) TYPE = MyISAM;
+#) ENGINE = MyISAM;
#################################################
# Upgrades
ALTER TABLE chanperm MODIFY `name` char(16) NOT NULL default '';
UPDATE chanperm SET name='AkickEnforce' WHERE name LIKE 'AkickEn%';
ALTER TABLE `ms_ignore` DROP KEY `nickid`, DROP COLUMN id;
-
-alter table user
- modify column id int(11) unsigned not null auto_increment,
- drop primary key,
- add primary key using btree (id),
- drop key nick,
- add key nick using hash (nick),
- drop key ip,
- add key using btree (ip);
-
-# Duplicate key given PRIMARY already indexes this column first.
-ALTER TABLE `nickalias` DROP KEY `root`;
-
-# Duplicate keys given PRIMARY already indexes this column first.
-ALTER TABLE `akick` DROP INDEX `chan`;
-ALTER TABLE `silence` DROP KEY `nick`;
-ALTER TABLE `nickid` DROP INDEX `id`, ADD KEY `nrid` (`nrid`);
-ALTER TABLE `watch` DROP KEY `nick`;
-
-ALTER TABLE `user` MODIFY `id` bigint unsigned NOT NULL default 0;
--- /dev/null
+#!/usr/bin/perl
+use strict;
+use warnings;
+
+use IO::Socket;
+use Time::HiRes qw(gettimeofday);
+my $socket = IO::Socket::INET->new(PeerAddr => '127.0.0.1',
+ PeerPort => 7000,
+ Proto => "tcp")
+ or die "Couldn't connect to localhost:7000 : $@\n";
+$socket->autoflush(1);
+&connected;
+my @serverlist;
+my %users;
+while ( <$socket> ) {
+ print "-> $_";
+ parsemsg($_);
+}
+
+sub connected {
+ # SERVER servername password hopcount id :description
+ print $socket "SERVER services.test.net polarbears 0 00A :Services \n";
+}
+sub parsemsg {
+ my $msg = $_;
+ $msg =~ s/[\r\n]//g;
+ if ($msg =~ /^SERVER (.*) (.*) (.*) (.*) :(.+)/) {
+ push @serverlist, $4;
+ ircsend(":00A BURST");
+ ircsend(":services.test.net VERSION :SurrealServices 00A");
+ ircsend(":00A UID 00AAAAAAB ".time." NickServ services.test.net services.test.net NickServ 0.0.0.0 ".time." +io :Nickname Services");
+ ircsend(":00AAAAAAB OPERTYPE Services");
+ ircsend(":00A UID 00AAAAAAC ".time." ChanServ services.test.net services.test.net ChanServ 0.0.0.0 ".time." +io :Channel Services");
+ ircsend(":00AAAAAAC OPERTYPE Services");
+ ircsend(":00A UID 00AAAAAAD ".time." MemoServ services.test.net services.test.net MemoServ 0.0.0.0 ".time." +io :Memo Services");
+ ircsend(":00AAAAAAD OPERTYPE Services");
+ ircsend(":00A UID 00AAAAAAE ".time." OperServ services.test.net services.test.net OperServ 0.0.0.0 ".time." +io :Oper Services");
+ ircsend(":00AAAAAAE OPERTYPE Services");
+ ircsend(":00A ENDBURST");
+ ircsend(":00A PING 00A $serverlist[0]");
+ }
+ if ($msg =~ /^:(.*) PING (.*) (.*)$/) {
+ if ($1 eq $serverlist[0]) {
+ ircsend(":00A PONG 00A $serverlist[0]");
+ }
+ }
+ if ($msg =~ /^:(.*) FJOIN (.*) (.*) (.+) :?(.+)$/) {
+ parse_fjoin($1,$2,$3,$4,$5);
+ }
+ if ($msg =~ /^:(.*) IDLE (.*)$/) {
+ parse_idle($1,$2);
+ }
+ if ($msg =~ /^:(.*) UID (\S+) (\d+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (.+) :(.+)$/) {
+ parse_uid($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11);
+ }
+ if ($msg =~ /^:(.*) PRIVMSG (\S+) :(.+)$/) {
+ parse_privmsg($1,$2,$3);
+ }
+}
+sub ircsend {
+ my $msg = shift;
+ print "<- $msg\n";
+ $msg .= " \n";
+ print $socket $msg;
+}
+
+sub parse_fjoin {
+ #:431 FJOIN #test 1246571540 +nt :,431AAAAAC ,431AAAAAA
+ my ($src, $chan, $ts, $modes, $users) = @_;
+ if ($chan eq "#test") {
+ print "!!! aa - $modes\n";
+ ircsend(":00A FJOIN $chan $ts $modes :o,00AAAAAAB o,00AAAAAAC o,00AAAAAAD o,00AAAAAAE");
+ }
+}
+sub parse_idle {
+ my ($src, $target) = @_;
+ ircsend(":$target IDLE $users{$src}{'nick'} ".time." 0");
+}
+sub parse_uid {
+ #:431 UID 431AAAAAA 1246349244 MusashiX90 127.0.0.1 netadmin.omega.org.za nano 127.0.0.1 1246349249 +Wios +ACJKLNOQacdfgjklnoqtx :mwt
+ my ($src, $uid, $ts, $nick, $hostname, $cloak, $ident, $ip, $signon, $modes, $realname) = @_;
+ print "DEBUG: Added '$nick' to users\n";
+ $users{$uid}{'nick'} = $nick;
+}
+
+sub parse_privmsg {
+ my ($src, $target, $msg) = @_;
+ # PRIVMSG sent to MemoServ
+ if ($target eq "00AAAAAAD") {
+ ircsend(":$target NOTICE $src :Received your message");
+ }
+}
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use MIME::Base64 qw( decode_base64 encode_base64 );
+use Socket;
+use Socket6;
+
+BEGIN {
+ use Cwd qw( abs_path getcwd );
+ use File::Basename qw( dirname );
+ use constant { PREFIX => abs_path(dirname(abs_path($0)).'/../') };
+}
+use lib PREFIX;
+
+use SrSv::Conf::main;
+use SrSv::IPv6;
+
+my $IPstring = 'AAAAAAAAAAAAAAAAAAAAAQ==';
+my $IPstring2 = 'CgECgw==';
+my $IPstring3 = 'IAEZOAJdvu8AAAAAAAEABA';
+
+#print length(decode_base64($IPstring)), "\n", length(decode_base64($IPstring2)), "\n";
+#exit;
+#print Socket6::inet_ntop(AF_INET6, decode_base64($IPstring)), "\n";
+#print Socket6::inet_ntop(AF_INET, decode_base64($IPstring2)), "\n";
+print Socket6::inet_ntop(AF_INET6, decode_base64($IPstring3)), "\n";
+print get_ipv6_net(Socket6::inet_ntop(AF_INET6, decode_base64($IPstring3))), "\n";
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+
+BEGIN {
+ use Cwd qw( abs_path getcwd );
+ use File::Basename qw( dirname );
+ use constant { PREFIX => abs_path(dirname(abs_path($0)).'/../') }
+}
+use lib PREFIX;
+
+use libs::misc;
+use SrSv::Util qw(say seqifyList makeSeqList);
+
+#say makeSeqList(92..99,1..3,5..9,);
+#say seqifyList(92..99,1..3,5..9,);
+say seqifyList(makeSeqList(92..99,1..3,5..9,10,11));
BEGIN {
use Cwd qw( abs_path getcwd );
use File::Basename qw( dirname );
- use constant { PREFIX => dirname(abs_path($0)), }
+ use constant { PREFIX => abs_path(dirname(abs_path($0)).'/../') }
}
use lib PREFIX;
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+
+BEGIN {
+ use Cwd qw( abs_path getcwd );
+ use File::Basename qw( dirname );
+ use constant { PREFIX => abs_path(dirname(abs_path($0)).'/../') }
+}
+use lib PREFIX;
+
+use SrSv::Time;
+
+my ($weeks, $days, $hours, $minutes, $seconds) = split_time(103.2);
+
+print "$minutes $seconds\n";
# This is based on an average observed from chanlogs.
# Thankfully bzcat and bzgrep tend to be agnostic.
my $bzip_threshold = 1000;
+# if less than 100 bytes, don't bother to gzip.
+my $gzip_threshold = 100;
opendir ((my $LOGDIR), $logdir.'/');
mkdir "$dir/$year/$month";
}
rename "$logdir/$filename", "$dir/$year/$month/$filename";
- system(((stat("$dir/$year/$month/$filename")->[7] > $bzip_threshold) ? $bzip2 : $gzip),
- '-9vv', "$dir/$year/$month/$filename");
+ compressFile("$dir/$year/$month/$filename");
$i++;
}
+
+sub compressFile($) {
+ my ($file) = @_;
+ my $fileStat = stat($file);
+ my $fileSize = $fileStat->[7];
+ my $compressor;
+ if($fileSize > $bzip_threshold) {
+ $compressor = $bzip2;
+ } elsif($fileSize < $gzip_threshold) {
+ return;
+ } else {
+ $compressor = $gzip;
+ }
+ system($compressor, '-9vv', $file);
+}
closedir $LOGDIR;
print "Processed $i logs\n";
use Date::Parse;
use SrSv::SimpleHash qw(readHash);
+use SrSv::Conf::sql;
use SrSv::Conf2Consts 'sql';
-my $srcname = 'http://www.dronebl.org/buildzone.do';
+my $srcname = 'http://dronebl.org/buildzone.do';
my $bindip = undef;
my $unpackname = $srcname;
my $diffname = $srcname.'.diff';
+my $agent = findAgent();
+
+sub findAgent {
+ my $agent;
+ my $ret = system('which curl');
+ if(($ret >> 8) == 0) {
+ # we prefer curl b/c it can handle gzip compression!
+ # we do IPv4 b/c either their IPv6 gateway or ours is SLOW
+ # UPDATE 2011/05: due to DDoS, IPv4 is swamped, IPv6 is only way!
+ $agent = 'curl --compressed --silent';
+ } else {
+ $agent = 'wget -q -O -';
+ }
+ return $agent;
+}
my $OPMDATA;
-unless(open $OPMDATA, '-|', "wget -q -O - http://www.dronebl.org/buildzone.do") {
+unless(open $OPMDATA, '-|', "$agent $srcname") {
print STDERR "FATAL: Processing failed.\n";
exit -1;
}
$dbh->do("DROP TABLE IF EXISTS `newopm`");
$dbh->do(
-"CREATE TABLE `newopm` (
+"CREATE TEMPORARY TABLE `newopm` (
`ipnum` int(11) unsigned NOT NULL default 0,
`ipaddr` char(15) NOT NULL default '0.0.0.0',
`type` tinyint(3) NOT NULL default 0,
PRIMARY KEY (`ipnum`),
UNIQUE KEY `addrkey` (`ipaddr`)
-) TYPE=MyISAM;"
+) Engine=Memory;"
);
sub save2DB($@) {
print "Inserting data... ";
$dbh->do("ALTER TABLE `newopm` DISABLE KEYS");
- $dbh->do("LOCK TABLES newopm WRITE");
+ $dbh->do("LOCK TABLES `newopm` WRITE");
my $type;
my $baseQuery = "REPLACE INTO `newopm` (ipnum, ipaddr, type) VALUES ";
my @rows;
my $count = 0;
while(my $x = <$OPMDATA>) {
chomp $x;
- if($x =~ /^:(\d+):$/) {
+ if($x =~ /^:(\d{1,3}):/) {
$type = $1;
} elsif($x =~ /^(\d+\.\d+\.\d+\.\d+)$/) {
next unless $type;
my $ipaddr = $1;
push @rows, '(INET_ATON('.$dbh->quote($ipaddr).'),'.$dbh->quote($ipaddr).','.$type.')';
$count++;
- if(scalar(@rows)) {
+ if(scalar(@rows) > 1000) {
save2DB($baseQuery, @rows);
@rows = ();
}
print "done.\nRemoving old table...\n";
$dbh->do("DROP TABLE IF EXISTS `oldopm`");
-$dbh->do("OPTIMIZE TABLE `newopm`");
+$dbh->do("ALTER TABLE opm ENGINE=InnoDB");
+$dbh->do("START TRANSACTION");
print "Renaming new table...\n";
-$dbh->{RaiseError} = $dbh->{PrintError} = 0; # the following commands can fail, but are harmless.
-$dbh->do("RENAME TABLE `opm` TO `oldopm`");
-$dbh->do("RENAME TABLE `newopm` TO `opm`");
-$dbh->do("DROP TABLE IF EXISTS `oldopm`");
+#$dbh->{RaiseError} = $dbh->{PrintError} = 0; # the following commands can fail, but are harmless.
+$dbh->do("TRUNCATE TABLE `opm`");
+$dbh->do("INSERT INTO opm SELECT * FROM newopm");
+$dbh->do("COMMIT");
print "Blacklist table update complete.\n";
use Date::Parse;
use SrSv::Conf 'sql';
+use SrSv::Conf::sql;
+use SrSv::Conf2Consts qw( sql );
my $countrydb_url= 'http://ip.ludost.net/raw/country.db.gz';
my $srcname = 'country.db.gz';
use Date::Parse;
-use SrSv::Conf 'sql';
+use SrSv::Conf::sql;
+use SrSv::Conf2Consts qw( sql );
use constant {
countrydb_url => 'http://www.maxmind.com/download/geoip/database/GeoIPCountryCSV.zip',
use Date::Parse;
-use SrSv::Conf 'sql';
+use SrSv::Conf::sql;
+use SrSv::Conf2Consts qw( sql );
use constant {
countrydb_url => 'rsync://countries-ns.mdc.dk/zone/zz.countries.nerd.dk.rbldnsd',
# Add tables to this list to be skipped
# SrSv wants to skip the country table
-our %skipList = ( 'country' => 1 );
+our %skipList = ( 'country' => 1, 'geoip' => 1, 'geolocation' => 1, 'georegion' => 1, 'opm' => 1 );
use constant {
DROP_TABLE => 1,
# WARNING: Doing this with hundred megabyte tables
# will probably be slow, and possibly DoS your system
# with an Out of Memory condition.
- LARGE_TABLES => 0,
+ LARGE_TABLES => 1,
# Most of the time, you don't want to preserve the contents
# of a MEMORY or HEAP table, since they're just temporary
# and would have been lost on a server restart anyway.
# Then again, maybe you want to keep them. If so, set this to 0.
# This does still save the schema.
- SKIP_HEAP_DUMP => 1,
-
+ SKIP_HEAP_DUMP => 0,
+
# This should only be used for debugging purposes
# as otherwise it throws junk into the output stream
VERBOSE => 0,
# or create a static hash table
sub get_sql_conn {
# These libs aren't needed for the generic case
+use SrSv::Conf::sql;
use SrSv::Conf2Consts qw( sql );
my %MySQL_config = (
if ($column_data->[++$i]->{TYPE_NAME} =~ /^(TEXT|BLOB)$/i and
length($element))
{
- $element = unpack ('H', $element);
+ $element = '0x' . unpack ('H*', $element);
}
elsif ($column_data->[$i]->{TYPE_NAME} =~ /int$/i and
length($element))
if ((SKIP_HEAP_DUMP) and
(($schema =~ /(ENGINE|TYPE)=(HEAP|MEMORY)/)) or ($skipList{lc $table})
) {
- next TABLE;
+ next TABLE;
}
}
--- /dev/null
+#!/usr/bin/perl
+
+# This file is part of SurrealServices.
+#
+# SurrealServices is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# SurrealServices is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with SurrealServices; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# SurrealChat.net does not provide the Country/Allocation data,
+# is in no way associated with maxmind.com,
+# nor are we providing a license to download/use it.
+# Be sure to direct availability/accuracy/licensing questions to maxmind.com
+
+use strict;
+#use warnings;
+use DBI;
+
+BEGIN {
+ use Cwd qw( abs_path getcwd );
+ use File::Basename;
+ my %constants = (
+ CWD => getcwd(),
+ PREFIX => abs_path(dirname(abs_path($0)).'/..'),
+ );
+ require constant; import constant(\%constants);
+}
+#chdir PREFIX;
+use lib PREFIX;
+
+use Date::Parse;
+use Text::ParseWords; # is a standard (in 5.8) module
+use Time::HiRes qw( time );
+
+use SrSv::Conf::sql;
+use SrSv::Conf2Consts qw( sql );
+use SrSv::Util qw( :say );
+use SrSv::Time qw( split_time );
+
+sub runSQL($@) {
+ my ($dbh, @strings) = @_;
+ foreach my $string (@strings) {
+ my $sql;
+ foreach my $x (split($/, $string)) { $sql .= $x unless $x =~ /^(#|--)/ or $x eq "\n"}
+# $dbh->do("START TRANSACTION");
+ my $printError = $dbh->{PrintError};
+ $dbh->{PrintError} = 0;
+ foreach my $line (split(/;/s, $sql)) {
+ next unless length($line);
+ #print "$line\n";
+ eval { $dbh->do($line); };
+ if($@) {
+ $line =~ s/\s{2,}/ /g;
+ $line =~ s/\n//g;
+ print "$line\n";
+ }
+
+ }
+ $dbh->{PrintError} = $printError;
+# $dbh->do("COMMIT");
+ }
+}
+
+BEGIN {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime();
+ $year += 1900;
+ $mon++; # gmtime returns months January=0
+ my $date = sprintf("%04d%02d01", $year, $mon);
+ require constant;
+ import constant {
+ #countrydb_url => 'http://www.maxmind.com/download/geoip/database/GeoIPCountryCSV.zip',
+ #FIXME: This needs a date generator!
+ countrydb_url => "http://www.maxmind.com/download/geoip/database/GeoLiteCity_CSV/GeoLiteCity_${date}.zip",
+ srcname => "GeoLiteCity_${date}.zip",
+ };
+}
+
+sub main() {
+ downloadData();
+ say "Connecting to database...";
+ my $dbh = dbConnect();
+ say "Creating new table...";
+ newTable($dbh);
+ say "Inserting data... ";
+ loadData($dbh);
+ print "Converting geoip table...";
+ convert($dbh);
+ cleanup($dbh);
+ $dbh->disconnect();
+ say "GeoIP update complete.";
+}
+
+main();
+exit 0;
+
+sub downloadData() {
+ # This MAY be implementable with an open of a pipe
+ # pipe the output of wget through gzip -d
+ # and then into the load-loop.
+ # It's a bit heavy to run directly from inside services however.
+ # I'd recommend it be run as a crontab script separate from services.
+
+ #return;
+ my ($stat, $date, $size);
+ my $srcPath = PREFIX.'/data/'.srcname;
+ say $srcPath;
+ use File::stat;
+ if($stat = stat($srcPath)) {
+ print "Checking for updated country data...\n";
+ my $header = qx "wget --spider -S @{[countrydb_url]} 2>&1";
+ ($date) = ($header =~ /Last-Modified: (.*)/);
+ ($size) = ($header =~ /Content-Length: (.*)/);
+ }
+
+ if($stat and $stat->size == $size and $stat->mtime >= str2time($date)) {
+ say "Country data is up to date.";
+ } else {
+# say $stat->size == $size;
+# say $stat->mtime >= str2time($date);
+ say "Downloading country data...";
+# return;
+
+ unlink $srcPath;
+ system('wget '.countrydb_url." -O $srcPath");
+ unless(-e $srcPath) {
+ sayERR "FATAL: Download failed.";
+ exit;
+ }
+ }
+
+ mkdir PREFIX.'/data/GeoIP/';
+ say "Decompressing...";
+ unlink(glob(PREFIX.'/data/GeoIP/Geo*.csv'));
+ system("unzip -j $srcPath -d ".PREFIX.'/data/GeoIP/');
+ unless(-f PREFIX.'/data/GeoIP/GeoLiteCity-Blocks.csv') {
+ sayERR "FATAL: Decompression failed.";
+ exit -1;
+ }
+}
+
+sub dbConnect() {
+ my $dbh;
+ eval {
+ $dbh = DBI->connect("DBI:mysql:".sql_conf_mysql_db, sql_conf_mysql_user, sql_conf_mysql_pass,
+ { AutoCommit => 1, RaiseError => 1 })
+ };
+
+ if($@) {
+ print STDERR "FATAL: Can't connect to database:\n$@\n";
+ print STDERR "You must edit config/sql.conf and create a corresponding\nMySQL user and database!\n\n";
+ exit -1;
+ }
+ return $dbh;
+}
+
+sub newTable($) {
+ my ($dbh) = @_;
+ $dbh->{RaiseError} = 1;
+ $dbh->{PrintError} = 1;
+
+ runSQL($dbh,
+ "DROP TABLE IF EXISTS new_geoip",
+ "CREATE TABLE `new_geoip` (
+ `low` int unsigned NOT NULL,
+ `high` int unsigned NOT NULL,
+ `location` mediumint(8) unsigned NOT NULL,
+ PRIMARY KEY (`low`, `high`)
+ ) Engine=MyISAM",
+
+ "DROP TABLE IF EXISTS new_geolocation",
+ #"locId,country,region,city,postalCode,latitude,longitude,metroCode,areaCode";
+ "CREATE TABLE `new_geolocation` (
+ `id` mediumint(8) unsigned NOT NULL,
+ `country` char(2) NOT NULL default '-',
+ `region` char(2) NOT NULL default '-',
+ `city` varchar(255) NOT NULL default '-',
+ `postalcode` varchar(6) NOT NULL default '-',
+ `latitude` float NOT NULL default 0.0,
+ `longitude` float NOT NULL default 0.0,
+ `metrocode` int unsigned NOT NULL default 0,
+ `areacode` int unsigned NOT NULL default 0,
+ PRIMARY KEY (`id`),
+ KEY `countrykey` (`country`)
+ ) Engine=MyISAM;",
+
+ "DROP TABLE IF EXISTS `new_metrocode`",
+ "CREATE TABLE `new_metrocode` (
+ `id` smallint NOT NULL default 0,
+ `metro` varchar(128) NOT NULL default '',
+ PRIMARY KEY (`id`)
+ ) Engine=MyISAM;",
+
+ "DROP TABLE IF EXISTS `new_geocountry`",
+ #"locId,country,region,city,postalCode,latitude,longitude,metroCode,areaCode";
+ "CREATE TABLE `new_geocountry` (
+ `code` char(2) NOT NULL default '',
+ `country` varchar(255) default '',
+ PRIMARY KEY (`code`)
+ ) Engine=MyISAM;",
+
+ "DROP TABLE IF EXISTS `new_georegion`",
+ #"locId,country,region,city,postalCode,latitude,longitude,metroCode,areaCode";
+ "CREATE TABLE `new_georegion` (
+ `country` char(2) NOT NULL default '',
+ `region` char(2) NOT NULL default '',
+ `name` varchar(255) default '',
+ PRIMARY KEY (`country`, `region`)
+ ) Engine=MyISAM;",
+
+ );
+}
+
+sub timeDiff($$) {
+ my ($time1, $time2) = @_;
+ my ($weeks, $days, $hours, $minutes, $seconds) = split_time($time2 - $time1);
+ return sprintf("%02d:%02.2f", $minutes, $seconds);
+}
+
+sub loadData($) {
+ my ($dbh) = @_;
+ $| = 1;
+=cut
+ my $unpackPath = PREFIX.'/data/'.unpackname;
+ my ($lines) = qx{wc -l $unpackPath};
+ my $div = int($lines/100);
+=cut
+ my ($i, @entries);
+ my $fh;
+ my $table;
+
+ my $time1 = time();
+ print "Loading geoip data...";
+####### geoip #######
+ open ($fh, '<', PREFIX.'/data/GeoIP/GeoLiteCity-Blocks.csv');
+ $table = 'geoip';
+ #my $add_entry = $dbh->prepare("INSERT INTO `new_geoip` (low, high, location) VALUES (?,?,?)");
+ runSQL($dbh,
+ "LOCK TABLES `new_geoip` WRITE, `new_geolocation` WRITE,
+ `new_metrocode` WRITE, `new_georegion` WRITE, `new_geocountry` WRITE",
+ "ALTER TABLE `new_$table` DISABLE KEYS",
+ );
+
+ my $columns = '(low, high, location)';
+ <$fh>; <$fh>; # pop first 2 lines off.
+ my $i = 0;
+ while(my $x = <$fh>) {
+ chomp $x;
+=cut
+ if($i == 0 or !($i % $div)) {
+ printf("\b\b\b\b%3d%", ($i/$lines)*100);
+ }
+=cut
+ my @args = split(',', $x);
+ push @entries, '(' . join(',', @args) . ')' if scalar(@args) == 3;
+ if(scalar(@entries) >= 100) { #1000 only gives another 10% boost for 10x as much memory
+ $dbh->do("INSERT INTO `new_$table` $columns VALUES ".join(',', @entries));
+ @entries = ();
+ print $i," \n";
+ }
+
+ $i++;
+ }
+ $dbh->do(("INSERT INTO `new_$table` $columns VALUES ".join(',', @entries))) if scalar(@entries);
+ $dbh->do("ALTER TABLE `new_$table` ENABLE KEYS");
+ @entries = ();
+ close $fh;
+####### END geoip #######
+ say " Done.";
+ my $time2 = time();
+ print " Done. "; say timeDiff($time1, $time2);
+
+ $time1 = time();
+ print "Loading location data...";
+####### locations #######
+ $table = 'geolocation';
+ $columns = "(`id`, `country`, `region`, `city`, `postalcode`, `latitude`, `longitude`, `metrocode`, `areacode`)";
+ open ($fh, '<', PREFIX.'/data/GeoIP/GeoLiteCity-Location.csv');
+
+ $dbh->do("ALTER TABLE `new_$table` DISABLE KEYS");
+
+ <$fh>; <$fh>; # pop first 2 lines off.
+ while(my $x = <$fh>) {
+ chomp $x;
+=cut
+ if($i == 0 or !($i % $div)) {
+ printf("\b\b\b\b%3d%", ($i/$lines)*100);
+ }
+=cut
+ my @args = map( { $dbh->quote($_) } parse_line(",\\s*", 0, $x) );
+ push @entries, '(' . join(',', @args) . ')' if scalar(@args) == 9;
+ if(scalar(@entries) >= 100) { #1000 only gives another 10% boost for 10x as much memory
+ $dbh->do("INSERT INTO `new_$table` $columns VALUES ".join(',', @entries));
+ @entries = ();
+ }
+
+ $i++;
+ }
+ $dbh->do(("INSERT INTO `new_$table` $columns VALUES ".join(',', @entries))) if scalar(@entries);
+ @entries = ();
+ $dbh->do("ALTER TABLE `new_$table` ENABLE KEYS");
+ close $fh;
+####### END locations #######
+ say " Done.";
+ $time2 = time();
+ print " Done. "; say timeDiff($time1, $time2);
+
+
+ $time1 = time();
+ print "Loading metrocode data...";
+####### metrocodes #######
+ open ($fh, '<', PREFIX.'/data/GeoIP/metrocodes.txt');
+ $table = 'metrocode';
+ $columns = "(`id`, `metro`)";
+
+ $dbh->do("ALTER TABLE `new_$table` DISABLE KEYS");
+
+ while(my $x = <$fh>) {
+ chomp $x;
+=cut
+ if($i == 0 or !($i % $div)) {
+ printf("\b\b\b\b%3d%", ($i/$lines)*100);
+ }
+=cut
+ my @args = map( { $dbh->quote($_) } split(' ', $x, 2) );
+ push @entries, '(' . join(',', @args) . ')' if scalar(@args) == 2;
+ if(scalar(@entries) >= 100) { #1000 only gives another 10% boost for 10x as much memory
+ $dbh->do("INSERT INTO `new_$table` $columns VALUES ".join(',', @entries));
+ @entries = ();
+ }
+
+ $i++;
+ }
+ $dbh->do(("INSERT INTO `new_$table` $columns VALUES ".join(',', @entries))) if scalar(@entries);
+ @entries = ();
+ $dbh->do("ALTER TABLE `new_$table` ENABLE KEYS");
+ close $fh;
+####### END metrocodes #######
+ say " Done.";
+ $time2 = time();
+ print " Done. "; say timeDiff($time1, $time2);
+
+ $time1 = time();
+ print "Loading region data...";
+####### regions #######
+ $table = 'georegion';
+ $columns = "(`country`, `region`, `name`)";
+
+ $dbh->do("ALTER TABLE `new_$table` DISABLE KEYS");
+ open ($fh, '<', PREFIX.'/data/fips10_4');
+ <$fh>; # pop first line off.
+ while(my $x = <$fh>) {
+ chomp $x;
+=cut
+ if($i == 0 or !($i % $div)) {
+ printf("\b\b\b\b%3d%", ($i/$lines)*100);
+ }
+=cut
+ my @args = map( { $dbh->quote($_) } parse_line(",\\s*", 0, $x) );
+ push @entries, '(' . join(',', @args) . ')' if scalar(@args) == 3;
+ if(scalar(@entries) >= 100) { #1000 only gives another 10% boost for 10x as much memory
+ $dbh->do("INSERT INTO `new_$table` $columns VALUES ".join(',', @entries));
+ @entries = ();
+ }
+
+ $i++;
+ }
+ close $fh;
+
+ open ($fh, '<', PREFIX.'/data/iso3166_2');
+ <$fh>; # pop first line off.
+ while(my $x = <$fh>) {
+ chomp $x;
+=cut
+ if($i == 0 or !($i % $div)) {
+ printf("\b\b\b\b%3d%", ($i/$lines)*100);
+ }
+=cut
+ my @args = map( { $dbh->quote($_) } parse_line(",\\s*", 0, $x) );
+ push @entries, '(' . join(',', @args) . ')' if scalar(@args) == 3;
+ if(scalar(@entries) >= 100) { #1000 only gives another 10% boost for 10x as much memory
+ $dbh->do("INSERT INTO `new_$table` $columns VALUES ".join(',', @entries));
+ @entries = ();
+ }
+
+ $i++;
+ }
+ close $fh;
+ $dbh->do(("INSERT INTO `new_$table` $columns VALUES ".join(',', @entries))) if scalar(@entries);
+ @entries = ();
+ $dbh->do("ALTER TABLE `new_$table` ENABLE KEYS");
+####### END regions #######
+ say " Done.";
+ $time2 = time();
+ print " Done. "; say timeDiff($time1, $time2);
+
+ $time1 = time();
+ print "Loading country data...";
+####### iso3166 Country Names #######
+ open ($fh, '<', PREFIX.'/data/iso3166');
+ $table = 'geocountry';
+ $columns = "(`code`, `country`)";
+
+ $dbh->do("ALTER TABLE `new_$table` DISABLE KEYS");
+
+ while(my $x = <$fh>) {
+ chomp $x;
+=cut
+ if($i == 0 or !($i % $div)) {
+ printf("\b\b\b\b%3d%", ($i/$lines)*100);
+ }
+=cut
+ my @args = map( { $dbh->quote($_) } parse_line(",\\s*", 0, $x) );
+ push @entries, '(' . join(',', @args) . ')' if scalar(@args) == 2;
+ if(scalar(@entries) >= 100) { #1000 only gives another 10% boost for 10x as much memory
+ $dbh->do("INSERT INTO `new_$table` $columns VALUES ".join(',', @entries));
+ @entries = ();
+ }
+
+ $i++;
+ }
+ $dbh->do(("INSERT INTO `new_$table` $columns VALUES ".join(',', @entries))) if scalar(@entries);
+ @entries = ();
+ $dbh->do("ALTER TABLE `new_$table` ENABLE KEYS");
+ close $fh;
+####### END iso3166 Country Names #######
+ say " Done.";
+ $time2 = time();
+ print " Done. "; say timeDiff($time1, $time2);
+
+
+ $dbh->do("UNLOCK TABLES");
+}
+
+sub convert($) {
+ my ($dbh) = @_;
+
+ my $time1 = time();
+ runSQL($dbh,
+ "DROP TABLE IF EXISTS `tmp_geoip`",
+ "RENAME TABLE `new_geoip` TO `tmp_geoip`",
+ "CREATE TABLE `new_geoip` (
+ `low` int unsigned NOT NULL,
+ `high` int unsigned NOT NULL,
+ `location` mediumint(8) unsigned NOT NULL,
+ `ip_poly` polygon not null,
+ PRIMARY KEY (`low`, `high`),
+ SPATIAL INDEX (`ip_poly`)
+ ) Engine=MyISAM",
+ "ALTER TABLE `new_geoip` DISABLE KEYS",
+ "INSERT INTO new_geoip (low,high,location,ip_poly)
+ SELECT low, high, location,
+ GEOMFROMWKB(POLYGON(LINESTRING( POINT(low, -1), POINT(high, -1),
+ POINT(high, 1), POINT(low, 1), POINT(low, -1)))) FROM tmp_geoip;",
+ "ALTER TABLE `new_geoip` ENABLE KEYS",
+ "DROP TABLE IF EXISTS `tmp_geoip`",
+ );
+ my $time2 = time();
+ print " Done. "; say timeDiff($time1, $time2);
+
+}
+
+sub cleanup($) {
+ my ($dbh) = @_;
+
+# print "\b\b\b\bdone.\nRemoving old table...\n";
+ $dbh->do("DROP TABLE IF EXISTS `oldcountry`");
+ say "Renaming new tables...";
+ $dbh->{RaiseError} = 0;
+ $dbh->{PrintError} = 0;
+ $dbh->do("OPTIMIZE TABLE `new_geoip`");
+ $dbh->do("ANALYZE TABLE `new_geoip`");
+ # Doing the renames cannot be done atomically
+ # as sometimes `country` doesn't exist yet.
+ $dbh->do("START TRANSACTION");
+ $dbh->do("RENAME TABLE `geoip` TO `old_geoip`");
+ $dbh->do("RENAME TABLE `new_geoip` TO `geoip`");
+
+ $dbh->do("RENAME TABLE `geolocation` TO `old_geolocation`");
+ $dbh->do("RENAME TABLE `new_geolocation` TO `geolocation`");
+
+ $dbh->do("RENAME TABLE `metrocode` TO `old_metrocode`");
+ $dbh->do("RENAME TABLE `new_metrocode` TO `metrocode`");
+
+ $dbh->do("RENAME TABLE `georegion` TO `old_georegion`");
+ $dbh->do("RENAME TABLE `new_georegion` TO `georegion`");
+
+ $dbh->do("RENAME TABLE `geocountry` TO `old_geocountry`");
+ $dbh->do("RENAME TABLE `new_geocountry` TO `geocountry`");
+
+ $dbh->do("DROP TABLE `old_geoip`");
+ $dbh->do("DROP TABLE `old_geolocation`");
+ $dbh->do("DROP TABLE `old_metrocode`");
+ $dbh->do("DROP TABLE `old_georegion`");
+ $dbh->do("DROP TABLE `old_geocountry`");
+ $dbh->do("COMMIT");
+ #unlink PREFIX.'/data/'.unpackname;
+}
use Date::Parse;
use Text::ParseWords; # is a standard (in 5.8) module
+use Time::HiRes qw( time );
+use SrSv::Conf::sql;
use SrSv::Conf2Consts qw( sql );
use SrSv::Util qw( :say );
+use SrSv::Time qw( split_time );
sub runSQL($@) {
my ($dbh, @strings) = @_;
newTable($dbh);
say "Inserting data... ";
loadData($dbh);
- say "Converting geoip table... ";
+ print "Converting geoip table...";
convert($dbh);
cleanup($dbh);
$dbh->disconnect();
$dbh->{PrintError} = 1;
runSQL($dbh,
- "DROP TABLE IF EXISTS new_geoip",
- "CREATE TABLE `new_geoip` (
- `low` int unsigned NOT NULL default 0,
- `high` int unsigned NOT NULL default 0,
- `location` int NOT NULL default '0',
+ "CREATE TEMPORARY TABLE `tmp_geoip` (
+ `low` int unsigned NOT NULL,
+ `high` int unsigned NOT NULL,
+ `location` mediumint(8) NOT NULL,
PRIMARY KEY (`low`, `high`)
- ) TYPE=MyISAM",
+ ) Engine=MyISAM",
"DROP TABLE IF EXISTS new_geolocation",
#"locId,country,region,city,postalCode,latitude,longitude,metroCode,areaCode";
"CREATE TABLE `new_geolocation` (
- `id` int unsigned NOT NULL default 0,
+ `id` mediumint(8) unsigned NOT NULL,
`country` char(2) NOT NULL default '-',
`region` char(2) NOT NULL default '-',
`city` varchar(255) NOT NULL default '-',
`areacode` int unsigned NOT NULL default 0,
PRIMARY KEY (`id`),
KEY `countrykey` (`country`)
- ) TYPE=MyISAM;",
-
- "DROP TABLE IF EXISTS `new_metrocode`",
+ ) Engine=MyISAM;",
+
+ "DROP TABLE IF EXISTS `new_metrocode`",
"CREATE TABLE `new_metrocode` (
`id` smallint NOT NULL default 0,
`metro` varchar(128) NOT NULL default '',
PRIMARY KEY (`id`)
- ) TYPE=MyISAM;",
+ ) Engine=MyISAM;",
"DROP TABLE IF EXISTS `new_geocountry`",
#"locId,country,region,city,postalCode,latitude,longitude,metroCode,areaCode";
`code` char(2) NOT NULL default '',
`country` varchar(255) default '',
PRIMARY KEY (`code`)
- ) TYPE=MyISAM;",
+ ) Engine=MyISAM;",
"DROP TABLE IF EXISTS `new_georegion`",
#"locId,country,region,city,postalCode,latitude,longitude,metroCode,areaCode";
`region` char(2) NOT NULL default '',
`name` varchar(255) default '',
PRIMARY KEY (`country`, `region`)
- ) TYPE=MyISAM;",
+ ) Engine=MyISAM;",
);
}
+sub timeDiff($$) {
+ my ($time1, $time2) = @_;
+ my ($weeks, $days, $hours, $minutes, $seconds) = split_time($time2 - $time1);
+ return sprintf("%02d:%02d.%02d", $minutes, int($seconds), 100*($seconds-int($seconds)));
+}
+
sub loadData($) {
my ($dbh) = @_;
$| = 1;
my $fh;
my $table;
+ my $time1 = time();
print "Loading geoip data...";
####### geoip #######
- open ($fh, '<', PREFIX.'/data/GeoIP/GeoLiteCity-Blocks.csv');
$table = 'geoip';
- #my $add_entry = $dbh->prepare("INSERT INTO `new_geoip` (low, high, location) VALUES (?,?,?)");
- runSQL($dbh,
-# "LOCK TABLES `new_geoip` WRITE, `new_geolocation` WRITE,
-# `new_metrocode` WRITE, `new_georegion` WRITE, `new_geocountry` WRITE",
- "ALTER TABLE `new_$table` DISABLE KEYS",
- );
-
- my $columns = '(low, high, location)';
- <$fh>; <$fh>; # pop first 2 lines off.
- while(my $x = <$fh>) {
- chomp $x;
-=cut
- if($i == 0 or !($i % $div)) {
- printf("\b\b\b\b%3d%", ($i/$lines)*100);
- }
-=cut
- my @args = split(',', $x);
- push @entries, '(' . join(',', @args) . ')' if scalar(@args) == 3;
- if(scalar(@entries) >= 100) { #1000 only gives another 10% boost for 10x as much memory
- $dbh->do("INSERT DELAYED INTO `new_$table` $columns VALUES ".join(',', @entries));
- @entries = ();
- }
-
- $i++;
- }
- $dbh->do(("INSERT DELAYED INTO `new_$table` $columns VALUES ".join(',', @entries))) if scalar(@entries);
- $dbh->do("ALTER TABLE `new_$table` ENABLE KEYS");
- @entries = ();
- close $fh;
+ $dbh->do("LOAD DATA LOCAL INFILE
+ '@{[PREFIX]}/data/GeoIP/GeoLiteCity-Blocks.csv'
+ INTO TABLE tmp_${table}
+ FIELDS TERMINATED BY ',' ENCLOSED BY '\"' IGNORE 2 LINES");
####### END geoip #######
- say " Done.";
+ my $time2 = time();
+ print " Done. "; say timeDiff($time1, $time2);
+ $time1 = time();
print "Loading location data...";
####### locations #######
$table = 'geolocation';
- $columns = "(`id`, `country`, `region`, `city`, `postalcode`, `latitude`, `longitude`, `metrocode`, `areacode`)";
- open ($fh, '<', PREFIX.'/data/GeoIP/GeoLiteCity-Location.csv');
-
- $dbh->do("ALTER TABLE `new_$table` DISABLE KEYS");
-
- <$fh>; <$fh>; # pop first 2 lines off.
- while(my $x = <$fh>) {
- chomp $x;
-=cut
- if($i == 0 or !($i % $div)) {
- printf("\b\b\b\b%3d%", ($i/$lines)*100);
- }
-=cut
- my @args = map( { $dbh->quote($_) } parse_line(",\\s*", 0, $x) );
- push @entries, '(' . join(',', @args) . ')' if scalar(@args) == 9;
- if(scalar(@entries) >= 100) { #1000 only gives another 10% boost for 10x as much memory
- $dbh->do("INSERT DELAYED INTO `new_$table` $columns VALUES ".join(',', @entries));
- @entries = ();
- }
-
- $i++;
- }
- $dbh->do(("INSERT DELAYED INTO `new_$table` $columns VALUES ".join(',', @entries))) if scalar(@entries);
- @entries = ();
- $dbh->do("ALTER TABLE `new_$table` ENABLE KEYS");
- close $fh;
+ $dbh->do("LOAD DATA LOCAL INFILE
+ '@{[PREFIX]}/data/GeoIP/GeoLiteCity-Location.csv'
+ INTO TABLE new_${table}
+ FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"' IGNORE 2 LINES");
####### END locations #######
- say " Done.";
+ $time2 = time();
+ print " Done. "; say timeDiff($time1, $time2);
+ $time1 = time();
print "Loading metrocode data...";
####### metrocodes #######
open ($fh, '<', PREFIX.'/data/GeoIP/metrocodes.txt');
$table = 'metrocode';
- $columns = "(`id`, `metro`)";
+ my $columns = "(`id`, `metro`)";
$dbh->do("ALTER TABLE `new_$table` DISABLE KEYS");
my @args = map( { $dbh->quote($_) } split(' ', $x, 2) );
push @entries, '(' . join(',', @args) . ')' if scalar(@args) == 2;
if(scalar(@entries) >= 100) { #1000 only gives another 10% boost for 10x as much memory
- $dbh->do("INSERT DELAYED INTO `new_$table` $columns VALUES ".join(',', @entries));
+ $dbh->do("INSERT INTO `new_$table` $columns VALUES ".join(',', @entries));
@entries = ();
}
$i++;
}
- $dbh->do(("INSERT DELAYED INTO `new_$table` $columns VALUES ".join(',', @entries))) if scalar(@entries);
+ $dbh->do(("INSERT INTO `new_$table` $columns VALUES ".join(',', @entries))) if scalar(@entries);
@entries = ();
$dbh->do("ALTER TABLE `new_$table` ENABLE KEYS");
close $fh;
####### END metrocodes #######
- say " Done.";
+ $time2 = time();
+ print " Done. "; say timeDiff($time1, $time2);
+ $time1 = time();
print "Loading region data...";
####### regions #######
$table = 'georegion';
$columns = "(`country`, `region`, `name`)";
- $dbh->do("ALTER TABLE `new_$table` DISABLE KEYS");
- open ($fh, '<', PREFIX.'/data/fips10_4');
- <$fh>; # pop first line off.
- while(my $x = <$fh>) {
- chomp $x;
-=cut
- if($i == 0 or !($i % $div)) {
- printf("\b\b\b\b%3d%", ($i/$lines)*100);
- }
-=cut
- my @args = map( { $dbh->quote($_) } parse_line(",\\s*", 0, $x) );
- push @entries, '(' . join(',', @args) . ')' if scalar(@args) == 3;
- if(scalar(@entries) >= 100) { #1000 only gives another 10% boost for 10x as much memory
- $dbh->do("INSERT DELAYED INTO `new_$table` $columns VALUES ".join(',', @entries));
- @entries = ();
- }
-
- $i++;
- }
- close $fh;
+ $dbh->do("LOAD DATA LOCAL INFILE
+ '@{[PREFIX]}/data/fips10_4'
+ INTO TABLE new_${table}
+ FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"' IGNORE 1 LINES");
- open ($fh, '<', PREFIX.'/data/iso3166_2');
- <$fh>; # pop first line off.
- while(my $x = <$fh>) {
- chomp $x;
-=cut
- if($i == 0 or !($i % $div)) {
- printf("\b\b\b\b%3d%", ($i/$lines)*100);
- }
-=cut
- my @args = map( { $dbh->quote($_) } parse_line(",\\s*", 0, $x) );
- push @entries, '(' . join(',', @args) . ')' if scalar(@args) == 3;
- if(scalar(@entries) >= 100) { #1000 only gives another 10% boost for 10x as much memory
- $dbh->do("INSERT DELAYED INTO `new_$table` $columns VALUES ".join(',', @entries));
- @entries = ();
- }
+ $dbh->do("LOAD DATA LOCAL INFILE
+ '@{[PREFIX]}/data/iso3166_2'
+ INTO TABLE new_${table}
+ FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"' IGNORE 1 LINES");
- $i++;
- }
- close $fh;
- $dbh->do(("INSERT DELAYED INTO `new_$table` $columns VALUES ".join(',', @entries))) if scalar(@entries);
- @entries = ();
- $dbh->do("ALTER TABLE `new_$table` ENABLE KEYS");
####### END regions #######
- say " Done.";
+ $time2 = time();
+ print " Done. "; say timeDiff($time1, $time2);
+ $time1 = time();
print "Loading country data...";
####### iso3166 Country Names #######
- open ($fh, '<', PREFIX.'/data/iso3166');
$table = 'geocountry';
- $columns = "(`code`, `country`)";
-
- $dbh->do("ALTER TABLE `new_$table` DISABLE KEYS");
-
- while(my $x = <$fh>) {
- chomp $x;
-=cut
- if($i == 0 or !($i % $div)) {
- printf("\b\b\b\b%3d%", ($i/$lines)*100);
- }
-=cut
- my @args = map( { $dbh->quote($_) } parse_line(",\\s*", 0, $x) );
- push @entries, '(' . join(',', @args) . ')' if scalar(@args) == 2;
- if(scalar(@entries) >= 100) { #1000 only gives another 10% boost for 10x as much memory
- $dbh->do("INSERT DELAYED INTO `new_$table` $columns VALUES ".join(',', @entries));
- @entries = ();
- }
-
- $i++;
- }
- $dbh->do(("INSERT DELAYED INTO `new_$table` $columns VALUES ".join(',', @entries))) if scalar(@entries);
- @entries = ();
- $dbh->do("ALTER TABLE `new_$table` ENABLE KEYS");
- close $fh;
+ $dbh->do("LOAD DATA LOCAL INFILE
+ '@{[PREFIX]}/data/iso3166'
+ INTO TABLE new_${table}
+ FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"' IGNORE 1 LINES");
####### END iso3166 Country Names #######
- say " Done.";
-
+ $time2 = time();
+ print " Done. "; say timeDiff($time1, $time2);
-# $dbh->do("UNLOCK TABLES");
+ $dbh->do("UNLOCK TABLES");
}
sub convert($) {
my ($dbh) = @_;
+ my $time1 = time();
runSQL($dbh,
- "DROP TABLE IF EXISTS `tmp_geoip`",
- "RENAME TABLE `new_geoip` TO `tmp_geoip`",
"CREATE TABLE `new_geoip` (
- `low` int unsigned NOT NULL default 0,
- `high` int unsigned NOT NULL default 0,
- `location` int NOT NULL default '0',
- `ip_poly` polygon not null,
+ `low` int unsigned NOT NULL,
+ `high` int unsigned NOT NULL,
+ `location` mediumint(8) NOT NULL,
+ `ip_poly` polygon NOT NULL,
PRIMARY KEY (`low`, `high`),
SPATIAL INDEX (`ip_poly`)
- ) TYPE=MyISAM",
+ ) Engine=MyISAM",
"ALTER TABLE `new_geoip` DISABLE KEYS",
- "INSERT DELAYED INTO new_geoip (low,high,location,ip_poly)
+ "INSERT INTO new_geoip (low,high,location,ip_poly)
SELECT low, high, location,
GEOMFROMWKB(POLYGON(LINESTRING( POINT(low, -1), POINT(high, -1),
POINT(high, 1), POINT(low, 1), POINT(low, -1)))) FROM tmp_geoip;",
"ALTER TABLE `new_geoip` ENABLE KEYS",
"DROP TABLE IF EXISTS `tmp_geoip`",
);
+ my $time2 = time();
+ print " Done. "; say timeDiff($time1, $time2);
+
}
sub cleanup($) {