]>
Commit | Line | Data |
---|---|---|
7b261bb8 | 1 | #!/usr/bin/perl |
2 | # | |
3 | # Copyright saturn@surrealchat.net | |
4 | # multiple feature-adds and code changes tabris@tabris.net | |
5 | # | |
6 | # Licensed under the GNU Public License | |
7 | # http://www.gnu.org/licenses/gpl.txt | |
8 | # | |
9 | ||
10 | package securitybot; | |
11 | ||
12 | use strict; | |
13 | no strict "refs"; | |
14 | use Time::HiRes qw(gettimeofday); | |
15 | ||
16 | use SrSv::Process::Init; | |
17 | use SrSv::IRCd::Event 'addhandler'; | |
18 | use SrSv::IRCd::State 'initial_synced'; | |
19 | use SrSv::Timer qw(add_timer); | |
20 | use SrSv::Time; | |
21 | use SrSv::Agent; | |
22 | use SrSv::HostMask qw( parse_hostmask ); | |
7b261bb8 | 23 | use SrSv::Conf2Consts qw(main sql); |
24 | use SrSv::SimpleHash qw(readHash writeHash); | |
25 | ||
26 | use SrSv::Log; | |
27 | ||
28 | use SrSv::User qw( get_user_nick ); | |
29 | use SrSv::User::Notice; | |
30 | use SrSv::Help qw( sendhelp ); | |
31 | ||
32 | use SrSv::MySQL '$dbh'; | |
33 | use SrSv::MySQL::Glob; | |
34 | ||
b0a0754f | 35 | use SrSv::Shared qw(%conf $torip %unwhois); |
7b261bb8 | 36 | |
b6cdbd26 | 37 | use SrSv::Process::InParent qw(list_conf loadconf saveconf update_tor_list); |
7b261bb8 | 38 | |
39 | use SrSv::TOR; | |
40 | ||
7b261bb8 | 41 | #this stuff needs to be put into files |
42 | our $sbnick = "SecurityBot"; | |
43 | our $ident = 'Security'; | |
44 | our $gecos = 'Security Monitor (you are being monitored)'; | |
45 | our $umodes = '+BHSdopqz'; | |
46 | our $vhost = 'services.SC.bot'; | |
47 | ||
48 | our ( | |
49 | $add_spamfilter, $del_spamfilter, $add_tklban, $del_tklban, | |
50 | $del_expired_tklban, $get_expired_tklban, | |
51 | ||
52 | $get_tklban, $get_spamfilter, | |
53 | $get_all_tklban, $get_all_spamfilter, | |
54 | ||
55 | $check_opm, | |
56 | ); | |
57 | ||
58 | loadconf(0); | |
59 | our $enabletor = $conf{'EnableTor'}; | |
60 | register(); | |
61 | ||
62 | addhandler('SEOS', undef, undef, "securitybot::start_timers"); | |
63 | addhandler('TKL', undef, undef, "securitybot::handle_tkl"); | |
64 | ||
65 | addhandler('PRIVMSG', undef, $sbnick, "securitybot::msghandle"); | |
66 | addhandler('NOTICE', undef, $sbnick, "securitybot::noticehandle"); | |
67 | addhandler('SENDSNO', undef, undef, "securitybot::snotice"); | |
68 | addhandler('GLOBOPS', undef, undef, "securitybot::globops"); | |
69 | addhandler('SMO', undef, undef, "securitybot::snotice"); | |
70 | ||
71 | if($conf{'EnableTor'} or $conf{'CTCPonConnect'} or $conf{'EnableOPM'}) { | |
72 | addhandler('NICKCONN', undef, undef, 'securitybot::nickconn'); | |
73 | addhandler('USERIP', undef, undef, 'securitybot::userip'); | |
74 | } | |
75 | ||
76 | proc_init { | |
77 | $add_tklban = $dbh->prepare_cached("REPLACE INTO tklban | |
78 | SET type=?, ident=?, host=?, setter=?, expire=?, time=?, reason=?"); | |
79 | $del_tklban = $dbh->prepare_cached("DELETE FROM tklban WHERE type=? AND ident=? AND host=?"); | |
80 | $add_spamfilter = $dbh->prepare_cached("REPLACE INTO spamfilter | |
81 | SET target=?, action=?, setter=?, expire=?, time=?, bantime=?, reason=?, mask=?"); | |
82 | $del_spamfilter = $dbh->prepare_cached("DELETE FROM spamfilter WHERE target=? AND action=? AND mask=?"); | |
83 | ||
84 | $del_expired_tklban = $dbh->prepare_cached("DELETE FROM tklban WHERE expire <= UNIX_TIMESTAMP() AND expire!=0"); | |
85 | $get_expired_tklban = $dbh->prepare_cached("SELECT type, ident, host, setter, expire, time, reason | |
86 | FROM tklban WHERE expire <= UNIX_TIMESTAMP() AND expire!=0"); | |
87 | ||
88 | $get_tklban = $dbh->prepare_cached("SELECT setter, expire, time, reason FROM tklban WHERE | |
89 | type=? AND ident=? AND host=?"); | |
90 | $get_spamfilter = $dbh->prepare_cached("SELECT time, reason FROM spamfilter WHERE target=? AND action=? AND mask=?"); | |
91 | ||
92 | $get_all_tklban = $dbh->prepare_cached("SELECT type, ident, host, setter, expire, time, reason | |
93 | FROM tklban ORDER BY type, time, host"); | |
94 | $get_all_spamfilter = $dbh->prepare_cached("SELECT target, action, setter, expire, time, bantime, reason, mask, managed | |
95 | FROM spamfilter ORDER BY time, mask"); | |
96 | ||
97 | $check_opm = $dbh->prepare_cached("SELECT 1 FROM opm WHERE ipaddr=?"); | |
98 | }; | |
99 | ||
100 | sub init { | |
40bf2790 | 101 | return if main::COMPILE_ONLY(); |
7b261bb8 | 102 | my $tmpdbh = DBI->connect( |
103 | "DBI:mysql:".sql_conf_mysql_db, | |
104 | sql_conf_mysql_user, | |
105 | sql_conf_mysql_pass, | |
106 | { | |
107 | AutoCommit => 1, | |
108 | RaiseError => 1 | |
109 | } | |
110 | ); | |
111 | $tmpdbh->do("TRUNCATE TABLE tklban"); | |
112 | $tmpdbh->do("TRUNCATE TABLE spamfilter"); | |
113 | $tmpdbh->disconnect(); | |
114 | } | |
115 | ||
116 | =cut | |
117 | my %snomasks = ( | |
118 | e => 'Eyes Notice', | |
119 | v => 'VHost Notice', | |
120 | # They're prefixed already. | |
121 | #S => 'Spamfilter', | |
122 | o => 'Oper-up Notice', | |
123 | ); | |
124 | =cut | |
125 | ||
126 | sub snotice($$$) { | |
127 | my ($server, $type, $msg) = @_; | |
128 | # $type = $snomasks{$type}; | |
129 | # diagmsg( ($type ? "[$type] " : '').$msg); | |
130 | diagmsg( $msg); | |
131 | } | |
132 | ||
133 | sub globops($$) { | |
134 | my ($src, $msg) = @_; | |
135 | diagmsg("Global -- from $src: $msg"); | |
136 | } | |
137 | ||
138 | sub register { | |
d2344bba | 139 | agent_connect($sbnick, $ident, undef, $umodes, $gecos); |
7b261bb8 | 140 | ircd::sqline($sbnick, 'Reserved for Services'); |
141 | ||
142 | agent_join($sbnick, main_conf_diag); | |
143 | ircd::setmode($sbnick, main_conf_diag, '+o', $sbnick); | |
144 | } | |
145 | ||
146 | sub start_timers { | |
147 | add_timer('', 5, __PACKAGE__, 'securitybot::start_timers2'); | |
148 | expire_tkl_timed(); | |
149 | } | |
150 | ||
151 | sub start_timers2 { | |
39dbe351 | 152 | update_tor_list_timed(3540) if $enabletor; |
7b261bb8 | 153 | #securitybot::ss2tkl::update_ss_timed(3300) if $conf{'EnableSS'}; |
154 | }; | |
155 | ||
156 | sub nickconn { | |
157 | my ($rnick, $time, $ident, $host, $vhost, $server, $modes, $gecos, $ip, $svsstamp) = @_[0,2..4,8,5,7,9,10,6]; | |
158 | ||
159 | goto OUT if ($svsstamp or $unwhois{lc $rnick}); | |
160 | ||
161 | if((initial_synced and $enabletor) or $conf{'EnableOPM'} or $conf{'BanCountry'} ) { | |
162 | if ($ip) { | |
163 | check_blacklists($rnick, $ip) or return; | |
164 | } | |
165 | else { | |
166 | ircd::userip($rnick) unless module::is_loaded('services'); | |
167 | } | |
168 | } | |
169 | ||
170 | if($conf{'CTCPonConnect'}) { | |
171 | my @ctcplist = split(/ /, $conf{'CTCPonConnect'}); | |
172 | foreach my $ctcp_msg (@ctcplist) { | |
173 | if(uc($ctcp_msg) eq 'PING') { | |
174 | my ($sec, $usec) = gettimeofday(); | |
175 | ircd::ctcp($sbnick, $rnick, 'PING', $sec, $usec); | |
176 | } else { | |
177 | ircd::ctcp($sbnick, $rnick, uc($ctcp_msg)); | |
178 | } | |
179 | } | |
180 | } | |
181 | OUT: | |
182 | $unwhois{lc $rnick} = 1 unless ($svsstamp or $ip); | |
183 | } | |
184 | ||
185 | sub userip { | |
186 | my($src, $rnick, $ip) = @_; | |
187 | ||
188 | return unless($unwhois{lc $rnick}); | |
189 | return unless($ip =~ /^\d{1,3}(\.\d{1,3}){3}$/); | |
190 | ||
191 | check_blacklists($rnick, $ip) or return; | |
192 | ||
193 | delete $unwhois{lc $rnick}; | |
194 | } | |
195 | ||
196 | sub check_opm($) { | |
197 | my ($ip) = @_; | |
198 | $check_opm->execute($ip); | |
199 | my ($ret) = $check_opm->fetchrow_array(); | |
200 | $check_opm->finish(); | |
201 | return $ret; | |
202 | } | |
203 | ||
204 | sub check_country($) { | |
205 | my ($ip) = @_; | |
206 | my $ccode; | |
207 | if(module::is_loaded('geoip')) { | |
208 | $ccode = geoip::get_ip_location($ip); | |
209 | } elsif(module::is_loaded('country')) { | |
210 | $ccode = country::get_ip_country_aton($ip); | |
211 | } | |
212 | foreach my $country (split(/[, ]+/, $conf{'BanCountry'})) { | |
213 | if (lc $ccode eq lc $country) { | |
214 | return country::get_country_long($country); | |
215 | } | |
216 | } | |
217 | return undef; | |
218 | } | |
219 | ||
220 | sub mk_banreason($$) { | |
221 | my ($reason, $ip) = @_; | |
222 | $reason =~ s/\$/$ip/g; | |
223 | return $reason; | |
224 | } | |
225 | ||
226 | sub check_blacklists($$) { | |
227 | my ($rnick, $ip) = @_; | |
228 | ||
b0a0754f | 229 | if(initial_synced and $enabletor && $torip->{$ip}) { |
7b261bb8 | 230 | if (lc $enabletor eq lc 'vhost') { |
231 | ircd::chghost($sbnick, $rnick, misc::gen_uuid(1, 20).'.session.tor'); | |
232 | } else { | |
233 | ircd::zline($sbnick, $ip, $conf{'ProxyZlineTime'}, $conf{'TorZlineReason'}); | |
234 | } | |
235 | return 0; | |
236 | } | |
237 | ||
238 | if($conf{'EnableOPM'} && check_opm($ip)) { | |
239 | ircd::zline($sbnick, $ip, $conf{'ProxyZlineTime'}, mk_banreason($conf{'OPMZlineReason'}, $ip)); | |
240 | return 0; | |
241 | } | |
242 | ||
8f076623 | 243 | sub hasGeoCountry() { |
244 | return module::is_loaded('country') || module::is_loaded('geoip'); | |
245 | } | |
246 | ||
247 | if($conf{'BanCountry'} && hasGeoCountry() && (my $country = check_country($ip))) { | |
7b261bb8 | 248 | ircd::zline($sbnick, $ip, $conf{'ProxyZlineTime'}, mk_banreason($conf{'CountryZlineReason'}, $country)); |
249 | return 0; | |
250 | } | |
251 | ||
252 | return 1; | |
253 | } | |
254 | ||
255 | sub update_tor_list_timed($) { | |
256 | my $time = shift; | |
257 | $time = 3600 unless $time; | |
258 | ||
259 | add_timer('', $time, __PACKAGE__, 'securitybot::update_tor_list_timed'); | |
260 | ||
39dbe351 | 261 | update_tor_list() if $enabletor; |
7b261bb8 | 262 | } |
263 | ||
264 | sub update_tor_list() { | |
6f1e56e4 | 265 | return unless (defined($conf{'TorServer'}) && length($conf{'TorServer'})); |
7b261bb8 | 266 | diagmsg( " -- Loading Tor server list."); |
267 | ||
268 | # path may be a local one if you run a tor-client. | |
269 | # most configs are /var/lib/tor/cached-directory | |
270 | my %newtorip; | |
b0a0754f | 271 | my @entries; |
272 | local $SIG{__DIE__} = undef; | |
273 | eval { | |
274 | @entries = getTorRouters($conf{'TorServer'}); | |
275 | }; | |
276 | if($@) { | |
277 | ircd::debug("SecurityBot failed to load TOR data", $@); | |
278 | return; | |
279 | } | |
280 | foreach my $torIP (@entries) { | |
7b261bb8 | 281 | $newtorip{$torIP} = 1; |
282 | } | |
283 | ||
284 | my $torcount = scalar(keys(%newtorip)); | |
285 | ||
286 | if($torcount > 0) { | |
b0a0754f | 287 | $torip = \%newtorip; |
7b261bb8 | 288 | diagmsg( " -- Finished loading Tor server list - $torcount servers found."); |
289 | } else { | |
290 | diagmsg( " -- Failed to load Tor server list, CHECK YOUR TorServer SETTING."); | |
291 | } | |
292 | } | |
293 | ||
294 | sub msghandle { | |
295 | my ($rnick, $dst, $msg) = @_; | |
296 | print join("\n", @_); | |
297 | my $user = { NICK => $rnick, AGENT => $sbnick }; | |
298 | unless (adminserv::is_ircop($user)) { | |
299 | notice($user, 'Permission Denied'); | |
300 | return; | |
301 | } | |
302 | ||
303 | if($msg =~ /^help/i) { | |
304 | my (undef, @args) = split(/ /, $msg); #discards first token 'help' | |
305 | sendhelp($user, 'securitybot', @args); | |
306 | } | |
307 | ||
308 | elsif($msg =~ /^notice (\S*) (.*)/i) { | |
309 | ircd::notice($sbnick, $1, $2); | |
310 | } | |
311 | ||
312 | elsif($msg =~ /^msg (\S*) (.*)/i) { | |
313 | ircd::privmsg($sbnick, $1, $2); | |
314 | } | |
315 | ||
316 | elsif($msg =~ /^raw (.*)/i) { | |
317 | if(!adminserv::is_svsop($user, adminserv::S_ROOT() )) { | |
318 | notice($user, 'You do not have sufficient rank for this command'); | |
319 | return; | |
320 | } | |
321 | ircd::ircsend($1); | |
322 | } | |
323 | ||
324 | elsif($msg =~ /^kill (\S*) (.*)/i) { | |
325 | ircd::irckill($sbnick, $1, $2); | |
326 | } | |
327 | ||
328 | elsif($msg =~ /^conf/i) { | |
329 | notice($user, "Configuration:", list_conf); | |
330 | } | |
331 | ||
332 | elsif($msg =~ /^set (\S+) (.*)/i) { | |
333 | if(!adminserv::is_svsop($user, adminserv::S_ROOT() )) { | |
334 | notice($user, 'You do not have sufficient rank for this command'); | |
335 | return; | |
336 | } | |
337 | ||
338 | my @p = ($1, $2); | |
339 | chomp $p[1]; | |
340 | ||
341 | if(update_conf($p[0], $p[1])) { | |
342 | notice($user, "Configuration: ".$p[0]." = ".$p[1]); | |
343 | } else { | |
344 | notice($user, "That value is read-only."); | |
345 | } | |
346 | } | |
347 | ||
348 | elsif($msg =~ /^save/i) { | |
349 | notice($user, "Saving configuration."); | |
350 | ||
351 | saveconf(); | |
352 | } | |
353 | ||
354 | elsif($msg =~ /^rehash/i) { | |
355 | notice($user, "Loading configuration."); | |
356 | ||
357 | loadconf(1); | |
358 | } | |
359 | ||
360 | elsif($msg =~ /^tssync/i) { | |
361 | ircd::tssync(); | |
362 | } | |
363 | ||
364 | elsif($msg =~ /^svsnick (\S+) (\S+)/i) { | |
365 | if(!adminserv::is_svsop($user, adminserv::S_ROOT() )) { | |
366 | notice($user, 'You do not have sufficient rank for this command'); | |
367 | return; | |
368 | } | |
369 | ircd::svsnick($sbnick, $1, $2); | |
370 | } | |
371 | ||
372 | elsif($msg =~ /^tor-update/i) { | |
373 | notice($user, "Updating Tor server list."); | |
374 | update_tor_list(); | |
375 | } | |
376 | =cut | |
377 | elsif($msg =~ /^ss-update/i) { | |
378 | notice($user, "Updating SS definitions."); | |
379 | securitybot::ss2tkl::update_ss(); | |
380 | } | |
381 | =cut | |
382 | elsif($msg =~ /^tkl/i) { | |
383 | sb_tkl($user, $msg); | |
384 | } | |
385 | } | |
386 | ||
387 | sub list_conf() { | |
388 | my @k = keys(%conf); | |
389 | my @v = values(%conf); | |
390 | my @reply; | |
391 | ||
392 | for(my $i=0; $i<@k; $i++) { | |
393 | push @reply, $k[$i]." = ".$v[$i]; | |
394 | } | |
395 | return @reply; | |
396 | } | |
397 | ||
398 | sub noticehandle { | |
399 | my ($rnick, $dst, $msg) = @_; | |
400 | ||
401 | if($msg =~ /^\x01(\S+)\s?(.*?)\x01?$/) { | |
402 | diagmsg( "Got $1 reply from $rnick: $2"); | |
403 | } | |
404 | } | |
405 | ||
406 | sub sb_tkl($$) { | |
407 | # This function is a hack to fit better our normal services coding style. | |
408 | # Better fix is to rewrite msghandle in another cleanup patch. | |
409 | my ($user, $msg) = @_; | |
410 | # We discard first token 'tkl' | |
411 | my $cmd; | |
412 | (undef, $cmd, $msg) = split(/ /, $msg, 3); | |
413 | if(lc($cmd) eq 'list') { | |
414 | if($msg) { | |
415 | sb_tkl_glob($user, $msg); | |
416 | } | |
417 | else { | |
418 | sb_tkl_list($user); | |
419 | } | |
420 | } | |
421 | elsif(lc($cmd) eq 'del') { | |
422 | unless($msg) { | |
423 | notice($user, "You have to specify at least one parameter"); | |
424 | } | |
425 | sb_tkl_glob_delete($user, $msg); | |
426 | } | |
427 | } | |
428 | ||
429 | sub sb_tkl_list($) { | |
430 | my ($user) = @_; | |
431 | my @reply; | |
432 | $get_all_tklban->execute(); | |
433 | while(my ($type, $ident, $host, $setter, $expire, $time, $reason) = $get_all_tklban->fetchrow_array()) { | |
434 | if($type eq 'Q') { | |
435 | #push @reply, "$type $host $setter"; | |
436 | next; | |
437 | } | |
438 | else { | |
439 | push @reply, "$type $ident\@$host $setter"; | |
440 | } | |
441 | $time = gmtime2($time); $expire = time_rel($expire - time()) if $expire; | |
442 | push @reply, " set: $time; ".($expire ? "expires in: $expire" : "Will not expire"); | |
443 | push @reply, " reason: $reason"; | |
444 | } | |
445 | $get_all_tklban->finish(); | |
446 | push @reply, "No results" unless @reply; | |
447 | ||
448 | notice($user, @reply); | |
449 | } | |
450 | ||
451 | sub sb_tkl_glob($$) { | |
452 | my ($user, $cmdline) = @_; | |
453 | ||
454 | my $sql_expr = "SELECT type, ident, host, setter, expire, time, reason FROM tklban "; | |
455 | ||
456 | my ($filters, $parms) = split(/ /, $cmdline, 2); | |
457 | my @filters = split(//, $filters); | |
458 | unless($filters[0] eq '+' or $filters[0] eq '-') { | |
459 | notice($user, "Invalid Syntax: First parameter must be a set of filters preceded by a + or -"); | |
460 | return; | |
461 | } | |
462 | my @args = misc::parse_quoted($parms); | |
463 | ||
464 | my ($success, $expr) = make_tkl_query(\@filters, \@args); | |
465 | unless ($success) { | |
466 | notice($user, "Error: $expr"); | |
467 | return; | |
468 | } | |
469 | $sql_expr .= $expr; | |
470 | ||
471 | my @reply; | |
472 | my $get_glob_tklban = $dbh->prepare($sql_expr); | |
473 | $get_glob_tklban->execute(); | |
474 | while(my ($type, $ident, $host, $setter, $expire, $time, $reason) = $get_glob_tklban->fetchrow_array()) { | |
475 | if($type eq 'Q') { | |
476 | #push @reply, "$type $host $setter"; | |
477 | next; | |
478 | } | |
479 | else { | |
480 | push @reply, "$type $ident\@$host $setter"; | |
481 | } | |
482 | $time = gmtime2($time); $expire = time_rel($expire - time()) if $expire; | |
483 | push @reply, " set: $time; ".($expire ? "expires in: $expire" : "Will not expire"); | |
484 | push @reply, " reason: $reason"; | |
485 | } | |
486 | $get_glob_tklban->finish(); | |
487 | ||
488 | push @reply, "No results" unless @reply; | |
489 | notice($user, @reply); | |
490 | } | |
491 | ||
492 | sub sb_tkl_glob_delete($$) { | |
493 | my ($user, $cmdline) = @_; | |
494 | ||
495 | my $sql_expr = "SELECT type, ident, host FROM tklban "; | |
496 | ||
497 | my ($filters, $parms) = split(/ /, $cmdline, 2); | |
498 | my @filters = split(//, $filters); | |
499 | unless($filters[0] eq '+' or $filters[0] eq '-') { | |
500 | notice($user, "Invalid Syntax: First parameter must be a set of filters preceded by a + or -"); | |
501 | return; | |
502 | } | |
503 | my @args = misc::parse_quoted($parms); | |
504 | ||
505 | my ($success, $expr) = make_tkl_query(\@filters, \@args); | |
506 | unless ($success) { | |
507 | notice($user, "Error: $expr"); | |
508 | return; | |
509 | } | |
510 | ||
511 | $sql_expr .= $expr; | |
512 | ||
513 | my $src = get_user_nick($user); | |
514 | my $get_glob_tklban = $dbh->prepare($sql_expr); | |
515 | $get_glob_tklban->execute(); | |
516 | while(my ($type, $ident, $host) = $get_glob_tklban->fetchrow_array()) { | |
517 | if($type eq 'G') { | |
518 | ircd::unkline($src, $ident, $host); | |
519 | } | |
520 | elsif($type eq 'Z') { | |
521 | ircd::unzline($src, $host); | |
522 | } | |
523 | } | |
524 | $get_glob_tklban->finish(); | |
525 | ||
526 | } | |
527 | ||
528 | sub make_tkl_query($$) { | |
529 | my ($parm1, $parm2) = @_; | |
530 | my @filters = @$parm1; my @args = @$parm2; | |
531 | ||
532 | my ($sign, $sql_expr, $sortby, $where, $and); | |
533 | while(my $filter = shift @filters) { | |
534 | my $condition; | |
535 | if ($filter eq '+') { | |
536 | $sign = +1; | |
537 | next; | |
538 | } | |
539 | elsif($filter eq '-') { | |
540 | $sign = 0; | |
541 | next; | |
542 | } | |
543 | ||
544 | my $parm = shift @args; | |
545 | unless (defined($parm)) { | |
546 | return (0, "Not enough arguments for filters."); | |
547 | } | |
548 | if($filter eq 'm') { | |
549 | my ($mident, $mhost) = parse_hostmask($parm); | |
550 | $mident = glob2sql($dbh->quote($mident)) if $mident; | |
551 | $mhost = glob2sql($dbh->quote($mhost)) if $mhost; | |
552 | ||
553 | $condition = ($mident ? ($sign ? '' : '!'). | |
554 | "(ident LIKE $mident) " : ''). | |
555 | ($mhost ? ($sign ? '' : '!'). | |
556 | "(host LIKE $mhost) " : ''); | |
557 | } | |
558 | elsif($filter eq 'r') { | |
559 | my $reason = $dbh->quote($parm); | |
560 | $reason = glob2sql($reason); | |
561 | $condition = ($sign ? '' : '!')."(reason LIKE $reason) "; | |
562 | ||
563 | } | |
564 | elsif($filter eq 's') { | |
565 | my $setter = $dbh->quote($parm); | |
566 | $setter = glob2sql($setter); | |
567 | $condition = ($sign ? '' : '!')."(setter LIKE $setter) "; | |
568 | ||
569 | } | |
570 | if($filter eq 'M') { | |
571 | my ($mident, $mhost) = parse_hostmask($parm); | |
572 | $mident = $dbh->quote($mident) if $mident; | |
573 | $mhost = $dbh->quote($mhost) if $mhost; | |
574 | $condition = ($mident ? ($sign ? '' : '!'). | |
575 | "(ident REGEXP $mident) " : ''). | |
576 | ($mhost ? ($sign ? '' : '!'). | |
577 | "(host REGEXP $mhost) " : ''); | |
578 | } | |
579 | elsif($filter eq 'R') { | |
580 | my $reason = $dbh->quote($parm); | |
581 | $condition = ($sign ? '' : '!')."(reason REGEXP $reason) "; | |
582 | ||
583 | } | |
584 | elsif($filter eq 'S') { | |
585 | my $setter = $dbh->quote($parm); | |
586 | $condition = ($sign ? '' : '!')."(setter REGEXP $setter) "; | |
587 | ||
588 | } | |
589 | elsif(lc $filter eq 'o') { | |
590 | $parm = lc $parm; | |
591 | next unless ($parm =~ /(type|ident|host|setter|expire|reason|time)/); | |
592 | if ($sortby) { | |
593 | $sortby .= ', '; | |
594 | } else { | |
595 | $sortby = 'ORDER BY '; | |
596 | } | |
597 | $sortby .= $parm.($sign ? ' ASC' : ' DESC'); | |
598 | next; | |
599 | } | |
600 | if (!$where) { | |
601 | $sql_expr .= 'WHERE '; | |
602 | $where = 1; | |
603 | } | |
604 | if ($and) { | |
605 | $sql_expr .= 'AND '; | |
606 | } else { | |
607 | $and = 1; | |
608 | } | |
609 | $sql_expr .= $condition if $condition; | |
610 | } | |
611 | if (scalar(@args)) { | |
612 | return (0, "Too many arguments for filters."); | |
613 | } | |
614 | return (1, $sql_expr.((defined $sortby and $sortby ne '') ? $sortby : 'ORDER BY type, time, host')); | |
615 | } | |
616 | ||
617 | sub get_tkl_type_name($) { | |
618 | my %tkltype = ( | |
619 | G => 'G:line', | |
620 | Z => 'GZ:line', | |
621 | s => 'Shun', | |
622 | Q => 'Q:line', | |
623 | ); | |
624 | return $tkltype{$_[0]}; | |
625 | }; | |
626 | ||
627 | sub get_filter_action_name($) { | |
628 | my %filteraction = ( | |
629 | Z => 'GZ:line', | |
630 | S => 'tempshun', | |
631 | s => 'shun', | |
632 | g => 'G:line', | |
633 | z => 'Z:line', | |
634 | k => 'K:line', | |
635 | K => 'Kill', | |
636 | b => 'Block', | |
637 | d => 'DCC Block', | |
638 | v => 'Virus Chan', | |
639 | w => 'Warn', | |
640 | #t => 'Test', # Should never show up, and not implemented in 3.2.4 yet. | |
641 | ); | |
642 | return $filteraction{$_[0]}; | |
643 | }; | |
644 | ||
645 | sub handle_tkl($$@) { | |
646 | my ($type, $sign, @parms) = @_; | |
647 | return unless defined ($dbh); | |
648 | if ($type eq 'G' or $type eq 'Z' or $type eq 's' or $type eq 'Q') { | |
649 | if ($sign == +1) { | |
650 | my ($ident, $host, $setter, $expire, $time, $reason) = @parms; | |
651 | $add_tklban->execute($type, $ident, $host, $setter, $expire, $time, $reason); | |
652 | $add_tklban->finish(); | |
653 | diagmsg( get_tkl_type_name($type)." added for $ident\@$host ". | |
654 | "from ($setter on ".gmtime2($time). | |
655 | ($expire ? ' to expire at '.gmtime2($expire) : ' does not expire').": $reason)") | |
656 | if initial_synced() and $type ne 'Q'; | |
657 | } | |
658 | elsif($sign == -1) { | |
659 | my ($ident, $host, $setter) = @parms; | |
660 | ||
661 | if ($type ne 'Q' and initial_synced()) { | |
662 | $get_tklban->execute($type, $ident, $host); | |
663 | my (undef, $expire, $time, $reason) = $get_tklban->fetchrow_array; | |
664 | $get_tklban->finish(); | |
665 | ||
666 | diagmsg( "$setter removed ".get_tkl_type_name($type)." $ident\@$host ". | |
667 | "set at ".gmtime2($time)." - reason: $reason"); | |
668 | } | |
669 | ||
670 | $del_tklban->execute($type, $ident, $host); | |
671 | $del_tklban->finish(); | |
672 | } | |
673 | } | |
674 | elsif($type eq 'F') { | |
675 | if($sign == +1) { | |
676 | my ($target, $action, $setter, $expire, $time, $bantime, $reason, $mask) = @parms; | |
677 | $add_spamfilter->execute($target, $action, $setter, $expire, $time, $bantime, $reason, $mask); | |
678 | $add_spamfilter->finish(); | |
679 | diagmsg( "Spamfilter added: '$mask' [target: $target] [action: ". | |
680 | get_filter_action_name($action)."] [reason: $reason] on ".gmtime2($time)."from ($setter)") | |
681 | if initial_synced(); | |
682 | } | |
683 | elsif($sign == -1) { | |
684 | # TKL - F u Z tabris!northman@tabris.netadmin.SCnet.ops 0 0 :do_not!use@mask | |
685 | my ($target, $action, $setter, $mask) = @parms; | |
686 | if(initial_synced()) { | |
687 | $get_spamfilter->execute($target, $action, $mask); | |
688 | my ($time, $reason) = $get_spamfilter->fetchrow_array; | |
689 | $get_spamfilter->finish(); | |
690 | $reason =~ tr/_/ /; | |
691 | diagmsg( "$setter removed Spamfilter (action: ".get_filter_action_name($action). | |
692 | ", targets: $target) (reason: $reason) '$mask' set at: ".gmtime2($time)); | |
693 | } | |
694 | $del_spamfilter->execute($target, $action, $mask); | |
695 | $del_spamfilter->finish(); | |
696 | } | |
697 | } | |
698 | } | |
699 | ||
700 | sub saveconf() { | |
701 | writeHash(\%conf, "config/securitybot/sb.conf"); | |
702 | } | |
703 | ||
704 | sub loadconf($) { | |
705 | my ($update) = @_; | |
706 | ||
707 | %conf = readHash("config/securitybot/sb.conf"); | |
708 | } | |
709 | ||
710 | sub update_conf($$) { | |
711 | my ($k, $v) = @_; | |
712 | ||
713 | return 0 if($k eq 'EnableTor'); | |
714 | ||
715 | $conf{$k} = $v; | |
716 | return 1; | |
717 | } | |
718 | ||
719 | sub expire_tkl() { | |
720 | $get_expired_tklban->execute(); | |
721 | while (my ($type, $ident, $host, $setter, $expire, $time, $reason) = $get_expired_tklban->fetchrow_array()) { | |
722 | if ($type eq 'G' or $type eq 'Z' or $type eq 's') { | |
723 | diagmsg( "Expiring ".get_tkl_type_name($type)." $ident\@$host ". | |
724 | "set by $setter at ".gmtime2($time)." - reason: $reason"); | |
725 | #$del_tklban->execute($type, $ident, $host); | |
726 | #$del_tklban->finish(); | |
727 | } | |
728 | } | |
729 | $get_expired_tklban->finish(); | |
730 | ||
731 | $del_expired_tklban->execute(); | |
732 | $del_expired_tklban->finish(); | |
733 | } | |
734 | ||
735 | sub expire_tkl_timed { | |
736 | my ($time) = @_; | |
737 | $time = 10 unless $time; | |
738 | ||
739 | add_timer('10', $time, __PACKAGE__, "securitybot::expire_tkl_timed"); | |
740 | ||
741 | expire_tkl(); | |
742 | } | |
743 | ||
744 | sub diagmsg(@) { | |
745 | ircd::privmsg($sbnick, main_conf_diag, @_); | |
746 | write_log('diag', '<'.main_conf_local.'>', @_); | |
747 | } | |
748 | ||
749 | sub end { } | |
750 | sub unload { saveconf(); } | |
751 | ||
752 | 1; |