use strict;
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 );
$drop_chantext, $drop_nicktext,
$get_host,
$get_host_inchan,
+ $add_tempban,
+ $get_expired_bans,
+ $del_tmpban,
+ $get_bantime,
+ $set_bantime,
);
sub init() {
$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=?");
-
+ $get_bantime = $dbh->prepare ("SELECT bantime FROM chanreg WHERE chan=?");
+ $set_bantime = $dbh->prepare ("UPDATE chanreg set bantime=? 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_tempban = $dbh->prepare ("INSERT INTO tmpban values (?,?,UNIX_TIMESTAMP()+?,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.
$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 user.nick from user left join chanuser on (user.id=chanuser.nickid) where user.host=? and chanuser.joined=1 and chanuser.chan=? and 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=?");
+ $get_expired_bans = $dbh->prepare("select * from tmpban where expiry < UNIX_TIMESTAMP()");
+ $del_tmpban = $dbh->prepare("delete from tmpban where channel=? and banmask like ?");
}
use SrSv::MySQL::Stub {
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, $user, $mask, $args) = @_;
+ my $expiry = 0;
+ my @argz = split (" ",$args);
+ $expiry = parse_time(shift @argz) if $argz[0] =~ /\+/;
+ #kickban($chan, $user, undef, 'No clones allowed in this channel.');
+ my $reason = join(' ', @argz);
+ $reason =~ s/^\:// if $reason;
+ #sub kickban($$$$;$;$) {
+ #my ($chan, $user, $mask, $reason) = @_;
+ kickban ($chan, $user, $mask, $reason, undef, $expiry);
+ #sub kickban($$$$;$;$;) {
+ #my ($chan, $user, $mask, $reason, $noflush, $expiry) = @_;
+}
sub clones_exist ($$) {
my ($user, $chan) = @_;
my $cn = $chan->{CHAN};
return;
}
my $nick = $user->{NICK};
- $get_host->execute($nick);
- my ($host) = $get_host->fetchrow_array;
- $get_host_inchan -> execute ($host,$cn,$nick);
- my ($joined) = $get_host_inchan->fetchrow_array;
+ my ($id) = get_user_id($user);
+ $get_host_inchan -> execute ($id,$cn);
+ my ($joined) = $get_host_inchan ->fetchrow_array;
if ($joined) {
return $joined;
}
notice($user, 'Syntax: REGISTER <#channel> [password] [description]');
}
}
-
+ elsif ($cmd =~ /^t((e)?mp)?b(an)?$/i) {
+ my @args = split(/\s+/, $msg, 4); shift @args;
+ my ($cn) = $args[0];
+ my ($target) = $args[1];
+ my ($reason) = $args[2];
+ my $chan = {CHAN=>$cn};
+ cs_tempban ($user, $chan, $target, $reason);
+ }
+
+
elsif($cmd =~ /^(?:[uvhas]op|co?f(ounder)?)$/i) {
my ($cn, $cmd2) = splice(@args, 0, 2);
my $chan = { CHAN => $cn };
cs_join($user, @args);
}
}
- elsif($cmd =~ /^topic/i) {
- my $cn = shift @args;
- my $chan = { CHAN => $cn };
- if($cmd =~ /^topicprepend$/i) {
- if (@args == 0) {
- notice($user, 'Syntax: TOPICAPPEND <#channel> <message>');
- } else {
- $msg =~ s/^topicappend $cn //i;
- cs_topicappend($user, $chan, 0, $msg);
- }
- } elsif($cmd =~ /^topicprepend$/i) {
- if (@args == 0) {
- notice($user, 'Syntax: TOPICPREPEND <#channel> <message>');
- } else {
- $msg =~ s/^topicprepend $cn //i;
- cs_topicappend($user, $chan, 1, $msg);
- }
- } elsif($cmd =~ /^topic$/i) {
- if (@args == 0) {
- notice($user, 'Syntax: TOPIC <#channel> <message|NONE>');
- } else {
- $msg =~ s/^topic $cn //i;
- cs_topic($user, $chan, $msg);
- }
+ 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 {
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',
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->execute($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; }
undef($enforcers{lc $cn});
botserv::bot_part_if_needed(undef(), $chan, "Channel dropped.");
}
+sub cs_tempban($$$;$) {
+ my ($user, $chan, $target, $reason) = @_;
+ if(ref($chan) ne 'HASH' || !defined($chan->{CHAN})) {
+ notice($user, "Invalid tempban command, no channel specified");
+ return;
+ }
+ my @argz = split (" ",$reason);
+ my $expiry;
+ $expiry = shift @argz if $argz[0] =~ /\+/;
+ $reason = join (" ", @argz);
+ 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 $cn = $chan->{CHAN};
+ $reason = "$expiry Requested by $src".($reason?": $reason":'' ) . " ($expiry)";
+ 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"],
+ );
+ my $peace = ({modes::splitmodes(get_modelock($chan))}->{Q}->[0] eq '+');
+ my @targets = split(/\,/, $target);
+ foreach $target (@targets) {
+ if($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");
+ tempban ($chan, undef, $target, $reason);
+ next;
+ }
+ my $tuser = { NICK => $target };
+ my $targetlevel = get_best_acc($tuser, $chan);
+ if(lc $target eq lc agent($chan) or adminserv::is_service($tuser)) {
+ push @{$errors[0]}, $target;
+ next;
+ }
+ if(!get_user_id($tuser)) {
+ push @{$errors[3]}, $target;
+ next;
+ }
+
+ if( ( ($peace and $targetlevel > 0) or ($srclevel <= $targetlevel) )
+ and not ($override && check_override($user, 'BAN', "tempban $cn $target")) )
+ {
+ push @{$errors[2]}, $target;
+ next;
+ }
+ tempban ($chan, $tuser, undef, $reason);
+ }
+ foreach my $errlist (@errors) {
+ if(@$errlist > 1) {
+ my $msg = shift @$errlist;
+
+ foreach my $e (@$errlist) { $e = "\002$e\002" }
+
+ notice($user,
+ "Cannot tempban ".
+ enum("or", @$errlist).
+ ": $msg"
+ );
+ }
+ }
+}
sub cs_kick($$$;$$) {
my ($user, $chan, $target, $ban, $reason) = @_;
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));
-}
-
-sub cs_topicappend {
- my ($user, $chan, $front, $topicappend) = @_;
- my $cn = $chan->{CHAN};
- $get_topic->execute($cn);
- my ($cur_topic, undef, undef) = $get_topic->fetchrow_array;
- $get_topic->finish();
- my $new_topic = $cur_topic;
- if ($cur_topic) {
- if($front) {
- $new_topic = $topicappend . ' | ' . $cur_topic;
- } else {
- $new_topic .= ' | ' . $topicappend;
- }
- }
- cs_topic ($user, $chan, $new_topic);
-}
+}
### MISCELLANEA ###
return $type."$nick!$ident\@$vhost";
}
-sub kickban($$$$;$) {
- my ($chan, $user, $mask, $reason, $noflush) = @_;
+sub kickban($$$$;$;$;) {
+ my ($chan, $user, $mask, $reason, $noflush, $expiry) = @_;
my $cn = $chan->{CHAN};
- my $nick = get_user_nick($user);
-
- return 0 if adminserv::is_service($user);
+ my $nick;
+ $nick = get_user_nick($user) if ($user);
+ if (!$user && !$mask) {
+ ircd::debug ("No user or mask given for /cs kickban!");
+ return;
+ }
+ return 0 if $user && adminserv::is_service($user);
my $agent = agent($chan);
unless($mask) {
$mask = make_banmask($chan, $user);
}
-
+
enforcer_join($chan) if (get_user_count($chan) <= 1);
ircd::setmode($agent, $cn, '+b', $mask);
+ if (!$expiry) {
+ $get_bantime -> execute ($cn);
+ ($expiry) = $get_bantime -> fetchrow_array();
+ }
+ if ($expiry) {
+ $add_tempban->execute ($cn, $mask, $expiry);
+ }
ircd::flushmodes() unless $noflush;
- ircd::kick($agent, $cn, $nick, $reason);
+ ircd::kick($agent, $cn, $nick, $reason) if ($nick);
return 1;
}
my ($accnick, $override) = can_do($chan, 'JOIN', $user, { ACC => $acc, NOREPLY => 1 });
unless ($acc > 0 || $override) {
if (clones_exist ($user, $chan)) {
- kickban($chan, $user, undef, 'No clones allowed in this channel.')
+ tempban($chan, $user, undef, "+60s No clones allowed in this channel.");
}
}
$add_ban->execute($cn, $arg, $src, $type);
} else {
$delete_ban->execute($cn, $arg, $type);
+ $del_tmpban -> execute ($cn, $arg);
}
}
-
+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 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);
set_user_flag_all(UF_FINISHED());
$unlock_tables->execute(); $unlock_tables->finish;
+ check_expired_bans();
}
1;