1 # This file is part of SurrealServices.
3 # SurrealServices is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
8 # SurrealServices is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with SurrealServices; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 use SrSv
::Timer
qw(add_timer);
22 use SrSv
::IRCd
::State
qw(get_server_state);
23 use SrSv
::IRCd
::Validate
qw( valid_server valid_nick );
26 use SrSv
::Text
::Format
qw(columnar);
30 use SrSv
::Conf2Consts
qw(main services);
32 use SrSv
::User
qw(get_user_nick get_user_id get_user_agent is_online get_user_info get_user_ip :flood);
33 use SrSv
::User
::Notice
;
34 use SrSv
::Help
qw( sendhelp );
36 use SrSv
::NickReg
::Flags
qw(NRF_NOHIGHLIGHT nr_chk_flag_user);
38 use SrSv
::MySQL
'$dbh';
46 *kill_user
= \
&nickserv
::kill_user
;
48 our $osnick_default = 'OperServ';
49 our $osnick = $osnick_default;
50 our $osuser = { NICK
=> $osnick, ID
=> ircd
::getAgentUuid
($osnick) };
57 $add_akill, $del_akill, $get_all_akills, $get_expired_akills,
58 $get_akill, $check_akill,
62 $add_qline, $del_qline, $get_all_qlines, $get_expired_qlines,
63 $get_qline, $check_qline,
65 $add_logonnews, $del_logonnews, $list_logonnews, $get_logonnews,
66 $consolidate_logonnews, $count_logonnews, $del_expired_logonnews,
68 $add_clone_exceptname, $add_clone_exceptserver, $add_clone_exceptip,
69 $del_clone_exceptname, $del_clone_exceptip,
70 $list_clone_exceptname, $list_clone_exceptserver, $list_clone_exceptip,
72 $get_clones_fromhost, $get_clones_fromnick, $get_clones_fromid, $get_clones_fromipv4,
76 $get_newusers, $get_newusers_noid
80 $osuser = { NICK
=> $osnick, ID
=> ircd
::getAgentUuid
($osnick) };
82 $add_akill = $dbh->prepare("INSERT INTO akill SET setter=?, mask=?, reason=?, time=?, expire=?");
83 $del_akill = $dbh->prepare("DELETE FROM akill WHERE mask=?");
84 $get_all_akills = $dbh->prepare("SELECT setter, mask, reason, time, expire FROM akill ORDER BY time ASC");
85 $get_akill = $dbh->prepare("SELECT setter, mask, reason, time, expire FROM akill WHERE mask=?");
86 $check_akill = $dbh->prepare("SELECT 1 FROM akill WHERE mask=?");
88 $get_expired_akills = $dbh->prepare("SELECT setter, mask, reason, time, expire FROM akill WHERE expire < UNIX_TIMESTAMP() AND expire!=0");
91 $add_qline = $dbh->prepare("INSERT INTO qline SET setter=?, mask=?, reason=?, time=?, expire=?");
92 $del_qline = $dbh->prepare("DELETE FROM qline WHERE mask=?");
93 $get_all_qlines = $dbh->prepare("SELECT setter, mask, reason, time, expire FROM qline ORDER BY time ASC");
94 $get_qline = $dbh->prepare("SELECT setter, mask, reason, time, expire FROM qline WHERE mask=?");
95 $check_qline = $dbh->prepare("SELECT 1 FROM qline WHERE mask=?");
97 $get_expired_qlines = $dbh->prepare("SELECT mask FROM qline WHERE expire < UNIX_TIMESTAMP() AND expire!=0");
99 $add_logonnews = $dbh->prepare("INSERT INTO logonnews SET setter=?, expire=?, type=?, id=?, msg=?, time=UNIX_TIMESTAMP()");
100 $del_logonnews = $dbh->prepare("DELETE FROM logonnews WHERE type=? AND id=?");
101 $list_logonnews = $dbh->prepare("SELECT setter, time, expire, id, msg FROM logonnews WHERE type=? ORDER BY id ASC");
102 $get_logonnews = $dbh->prepare("SELECT setter, time, msg FROM logonnews WHERE type=? ORDER BY id ASC");
103 $consolidate_logonnews = $dbh->prepare("UPDATE logonnews SET id=id-1 WHERE type=? AND id>?");
104 $count_logonnews = $dbh->prepare("SELECT COUNT(*) FROM logonnews WHERE type=?");
105 $del_expired_logonnews = $dbh->prepare("DELETE FROM logonnews WHERE expire < UNIX_TIMESTAMP() AND expire!=0");
107 $add_clone_exceptname = $dbh->prepare("REPLACE INTO sesexname SET host=?, serv=0, adder=?, lim=?");
108 $add_clone_exceptserver = $dbh->prepare("REPLACE INTO sesexname SET host=?, serv=1, adder=?, lim=?");
109 $add_clone_exceptip = $dbh->prepare("REPLACE INTO sesexip SET ip=INET_ATON(?), mask=?, adder=?, lim=?");
111 $del_clone_exceptname = $dbh->prepare("DELETE FROM sesexname WHERE host=?");
112 $del_clone_exceptip = $dbh->prepare("DELETE FROM sesexip WHERE ip=INET_ATON(?)");
114 $list_clone_exceptname = $dbh->prepare("SELECT host, adder, lim FROM sesexname WHERE serv=0 ORDER BY host ASC");
115 $list_clone_exceptserver = $dbh->prepare("SELECT host, adder, lim FROM sesexname WHERE serv=1 ORDER BY host ASC");
116 $list_clone_exceptip = $dbh->prepare("SELECT INET_NTOA(ip), mask, adder, lim FROM sesexip ORDER BY ip ASC");
118 $get_clones_fromhost = $dbh->prepare("SELECT user.nick, user.id, user.online
119 FROM user JOIN user AS clone ON (user.ip=clone.ip)
120 WHERE clone.host=? GROUP BY id");
121 $get_clones_fromnick = $dbh->prepare("SELECT user.nick, user.id, user.online
122 FROM user JOIN user AS clone ON (user.ip=clone.ip)
123 WHERE clone.nick=? GROUP BY id");
124 $get_clones_fromid = $dbh->prepare("SELECT user.nick, user.id, user.online
125 FROM user JOIN user AS clone ON (user.ip=clone.ip)
126 WHERE clone.id=? GROUP BY id");
127 $get_clones_fromipv4 = $dbh->prepare("SELECT user.nick, user.id, user.online
128 FROM user JOIN user AS clone ON (user.ip=clone.ip)
129 WHERE clone.ip=INET_ATON(?) GROUP BY id");
131 $get_session_list = $dbh->prepare("SELECT host, COUNT(*) AS c FROM user WHERE online=1 GROUP BY host HAVING c >= ?");
133 $get_newusers = $dbh->prepare("SELECT user.nick, user.id, user.online
135 WHERE user.time > ?");
136 $get_newusers_noid = $dbh->prepare("SELECT user.nick, user.id, user.online
137 FROM user LEFT JOIN nickid ON (nickid.id=user.id)
138 WHERE nickid.id IS NULL AND user.time > ?");
142 $osuser = { NICK
=> $osnick, ID
=> ircd
::getAgentUuid
($osnick) };
143 my ($user, $dstUser, $msg) = @_;
145 my @args = split(/\s+/, $msg);
146 my $cmd = shift @args;
147 $user -> {AGENT
} = $osuser;
148 my $src = $user -> {NICK
};
149 return unless (lc $dstUser->{NICK
} eq lc $osnick);
151 services
::ulog
($osnick, LOG_INFO
(), "cmd: [$msg]", $user);
153 return if flood_check
($user);
154 unless(defined(adminserv
::is_svsop
($user)) or adminserv
::is_ircop
($user)) {
155 notice
($user, $err_deny);
156 if($cmd =~ /^set/i) {
157 nickserv
::kill_user
($user, "OS SET doesn't exist here");
159 ircd
::globops
($osuser, "\002$src\002 failed access to $osnick $msg");
163 if ($cmd =~ /^fjoin$/i) { os_fjoin
($user, @args); }
164 elsif ($cmd =~ /^fpart$/i) { os_fpart
($user, @args); }
165 elsif ($cmd =~ /^unidentify$/i) { os_unidentify
($user, @args); }
166 elsif ($cmd =~ /^qline$/i) {
167 my $cmd2 = shift @args;
169 if($cmd2 =~ /^add$/i) {
170 if(@args >= 3 and $args[0] =~ /^\+/) {
171 @args = split(/\s+/, $msg, 5);
173 os_qline_add
($user, @args[2..4]);
176 @args = split(/\s+/, $msg, 4);
178 os_qline_add
($user, 0, @args[2..3]);
181 notice
($user, 'Syntax: QLINE ADD [+expiry] <mask> <reason>');
184 elsif($cmd2 =~ /^del$/i) {
186 os_qline_del
($user, $args[0]);
189 notice
($user, 'Syntax: QLINE DEL <mask>');
192 elsif($cmd2 =~ /^list$/i) {
194 os_qline_list
($user);
197 notice
($user, 'Syntax: QLINE LIST');
201 elsif ($cmd =~ /^jupe$/i) {
203 os_jupe
($user, shift @args, join(' ', @args));
206 notice
($user, 'Syntax: JUPE <server> <reason>');
209 elsif ($cmd =~ /^uinfo$/i) { os_uinfo
($user, @args); }
210 elsif ($cmd =~ /^ninfo$/i) { os_ninfo
($user, @args); }
211 elsif ($cmd =~ /^svsnick$/i) { os_svsnick
($user, $args[0], $args[1]); }
212 elsif ($cmd =~ /^gnick$/i) { os_gnick
($user, @args); }
213 elsif ($cmd =~ /^help$/i) { sendhelp
($user, 'operserv', @args) }
214 elsif ($cmd =~ /^(staff|listadm)$/i) { adminserv
::as_staff
($user) }
215 elsif ($cmd =~ /^logonnews$/i) {
216 my $cmd2 = shift @args;
218 if($cmd2 =~ /^add$/i) {
219 if(@args >= 3 and $args[1] =~ /^\+/) {
220 @args = split(/\s+/, $msg, 5);
222 os_logonnews_add
($user, $args[2], $args[3], $args[4]);
225 @args = split(/\s+/, $msg, 4);
227 os_logonnews_add
($user, $args[2], 0, $args[3]);
230 notice
($user, 'Syntax: LOGONNEWS ADD <type> [+expiry] <reason>');
233 elsif($cmd2 =~ /^del$/i) {
235 os_logonnews_del
($user, $args[0], $args[1]);
238 notice
($user, 'Syntax: LOGONNEWS DEL <type> <id>');
241 elsif($cmd2 =~ /^list$/i) {
243 os_logonnews_list
($user, $args[0]);
246 notice
($user, 'Syntax: LOGONNEWS LIST <type>');
250 notice
($user, 'Syntax: LOGONNEWS <LIST|ADD|DEL> <type>');
253 elsif($cmd =~ /^except(ion)?$/i) {
254 my $cmd2 = shift @args;
255 if($cmd2 =~ /^server$/i) {
256 my $cmd3 = shift @args;
257 if($cmd3 =~ /^a(dd)?$/) {
259 os_except_server_add
($user, $args[0], $args[1]);
262 notice
($user, 'Syntax EXCEPT SERVER ADD <hostname> <limit>');
265 elsif($cmd =~ /^d(el)?$/) {
267 os_except_server_del
($user, $args[0]);
270 notice
($user, 'Syntax EXCEPT SERVER DEL <hostname>');
274 notice
($user, 'Syntax EXCEPT SERVER <ADD|DEL>');
277 elsif($cmd2 =~ /^h(ostname)?$/i) {
278 my $cmd3 = shift @args;
279 if($cmd3 =~ /^a(dd)?$/) {
281 os_except_hostname_add
($user, $args[0], $args[1]);
284 notice
($user, 'Syntax EXCEPT HOSTNAME ADD <hostname> <limit>');
287 elsif($cmd3 =~ /^d(el)?$/) {
289 os_except_hostname_del
($user, $args[0]);
292 notice
($user, 'Syntax EXCEPT HOSTNAME DEL <hostname>');
296 notice
($user, 'Syntax EXCEPT HOSTNAME <ADD|DEL>');
299 elsif($cmd2 =~ /^i(p)?$/i) {
300 my $cmd3 = shift @args;
301 if($cmd3 =~ /^a(dd)?$/) {
303 os_except_IP_add
($user, $args[0], $args[1]);
306 notice
($user, 'Syntax EXCEPT IP ADD <IP/mask> <limit>');
309 elsif($cmd3 =~ /^d(el)?$/) {
311 os_except_IP_del
($user, $args[0]);
314 notice
($user, 'Syntax EXCEPT IP DEL <IP>');
318 notice
($user, 'Syntax EXCEPT IP <ADD|DEL>');
321 elsif($cmd2 =~ /^l(ist)?$/i) {
323 os_except_list
($user);
326 notice
($user, 'Syntax EXCEPT LIST');
330 notice
($user, 'Syntax: EXCEPT <SERVER|HOSTNAME|IP|LIST>');
333 elsif($cmd =~ /^session$/i) {
335 os_session_list
($user, $args[0]);
337 notice
($user, 'Syntax SESSION <lim>');
340 elsif($cmd =~ /^chankill$/i) {
342 (undef, @args) = split(/\s+/, $msg, 3);
343 os_chankill
($user, @args);
345 notice
($user, 'Syntax: CHANKILL <#chan> <reason>');
348 elsif ($cmd =~ /^rehash$/i) {
350 os_rehash
($user, @args);
353 notice
($user, 'Syntax: REHASH [type]');
356 elsif ($cmd =~ /^loners$/i) {
357 os_loners
($user, @args);
359 elsif($cmd =~ /^svskill$/i) {
361 os_svskill
($user, shift @args, join(' ', @args));
364 notice
($user, 'Syntax SVSKILL <target> <reason here>');
367 elsif($cmd =~ /^kill$/i) {
369 os_kill
($user, shift @args, join(' ', @args));
372 notice
($user, 'Syntax KILL <target> <reason here>');
375 elsif ($cmd =~ /^clones$/i) {
376 os_clones
($user, @args);
378 elsif ($cmd =~ /^m(ass)?kill$/i) {
379 os_clones
($user, 'KILL', @args);
381 elsif($cmd =~ /^(kline|gline)$/i) {
383 os_gline
($user, 0, @args);
386 notice
($user, 'Syntax GLINE <target> [+time] [reason here]');
389 elsif($cmd =~ /^(zline|gzline)$/i) {
391 os_gline
($user, 1, @args);
394 notice
($user, 'Syntax GZLINE <target> [+time] [reason here]');
397 elsif ($cmd =~ /^killnew$/i) {
398 os_killnew
($user, @args);
401 else { notice
($user, "Unknown command."); }
405 my ($user, $target, @chans) = @_;
406 if ((!$target or !@chans) or !($chans[0] =~ /^#/)) {
407 notice
($user, "Syntax: /OS FJOIN <nick> <#channel1> [#channel2]");
409 unless (is_online
($target)) {
410 notice
($user, "\002$target\002 is not online");
414 if (!adminserv
::can_do
($user, 'FJOIN')) {
415 notice
($user, "You don't have the right access");
416 return $event::SUCCESS
;
418 my $tuser = {NICK
=>$target};
419 get_user_id
($tuser);
420 ircd
::svsjoin
($osuser, $tuser, @chans);
424 my ($user, $target, @params) = @_;
425 if ((!$target or !@params) or !($params[0] =~ /^#/)) {
426 notice
($user, "Syntax: /OS FPART <nick> <#channel1> [#channel2] [reason]");
428 unless (is_online
($target)) {
429 notice
($user, "\002$target\002 is not online");
433 if (!adminserv
::can_do
($user, 'FJOIN')) {
434 notice
($user, "You don't have the right access");
435 return $event::SUCCESS
;
438 my ($reason, @chans);
439 while ($params[0] =~ /^#/) {
440 push @chans, shift @params;
442 $reason = join(' ', @params) if @params;
444 ircd
::svspart
($osuser, $target, $reason, @chans);
447 sub os_qline_add
($$$$) {
448 my ($user, $expiry, $mask, $reason) = @_;
450 chk_auth
($user, 'QLINE') or return;
452 $expiry = parse_time
($expiry);
453 if($expiry) { $expiry += time() }
456 $check_qline->execute($mask);
457 if ($check_qline->fetchrow_array) {
458 notice
($user, "$mask is already qlined");
459 return $event::SUCCESS
;
461 my $src = get_user_nick
($user);
462 $add_qline->execute($src, $mask, $reason, time(), $expiry);
463 ircd
::sqline
($mask, $reason);
464 notice
($user, "$mask is now Q:lined");
468 sub os_qline_del
($$) {
469 my($user, $mask) = @_;
471 chk_auth
($user, 'QLINE') or return;
473 $check_qline->execute($mask);
474 if($check_qline->fetchrow_array) {
475 $del_qline->execute($mask);
476 ircd
::unsqline
($mask);
477 notice
($user, "$mask unqlined");
479 notice
($user, "$mask is not qlined");
483 sub os_qline_list
($) {
487 chk_auth
($user, 'QLINE') or return;
489 push @reply, 'Q:line list:';
491 $get_all_qlines->execute();
493 while (my ($setter, $mask, $reason, $time, $expiry) = $get_all_qlines->fetchrow_array) {
495 my $akill_entry1 = " $i) \002$mask\002 $reason";
496 my $akill_entry2 = " set by $setter on ".gmtime2
($time).'; ';
498 my ($weeks, $days, $hours, $minutes, $seconds) = split_time
($expiry-time
());
499 $akill_entry2 .= "Expires in ".($weeks?"$weeks weeks ":'').
500 ($days?"$days days ":'').
501 ($hours?"$hours hours ":'').
502 ($minutes?"$minutes minutes ":'');
505 $akill_entry2 .= "Does not expire.";
507 push @reply, $akill_entry1; push @reply, $akill_entry2;
509 $get_all_qlines->finish();
512 notice
($user, @reply) if @reply;
516 # introduces fake server to network.
517 my ($user, $server, $reason) = @_;
519 unless (adminserv
::is_svsop
($user, adminserv
::S_ROOT
())) {
520 notice
($user, $err_deny);
521 return $event::SUCCESS
;
523 unless (valid_server
($server)) {
524 notice
($user, "$server is not a valid servername.");
525 return $event::SUCCESS
;
527 if (get_server_state
($server)) {
528 notice
($user, "$server is currently connected. You must SQUIT before using JUPE.");
529 return $event::SUCCESS
;
532 ircd
::jupe_server
($server, "Juped by ".get_user_nick
($user).": $reason");
533 notice
($user, "$server is now juped.");
534 return $event::SUCCESS
;
537 sub os_unidentify
($$) {
538 my ($user, $tnick) = @_;
540 my $tuser = { NICK
=> $tnick };
543 unless ($tuid = get_user_id
($tuser)) {
544 notice
($user, "\002$tnick\002 is not online");
546 unless (adminserv
::can_do
($user, 'SERVOP')) {
547 notice
($user, $err_deny);
549 $nickserv::logout-
>execute($tuid);
550 notice
($user, "$tnick logged out from all nick identifies");
554 my ($user, @targets) = @_;
558 foreach my $target (@targets) {
560 push @userlist, $target;
563 if($target =~ /\,/) {
564 push @targets, split(',', $target);
568 my $tuser = { NICK
=> $target };
569 my $tuid = get_user_id
($tuser);
571 push @reply, "\002$target\002: user not found";
574 push @userlist, $tuser;
576 @targets = (); # drop this list now.
578 notice
($user, @reply, get_uinfo
($user, @userlist));
579 return $event::SUCCESS
;
583 my ($user, @targetsIn) = @_;
585 my (@targetsOut, @reply);
586 foreach my $target (@targetsIn) {
587 if(not nickserv
::is_registered
($target)) {
588 push @reply, "\002$target\002: is not registered.";
590 my @targets = SrSv
::NickReg
::User
::get_nick_users_all
($target);
591 if(scalar(@targets) == 0) {
592 push @reply, "\002$target\002: no user[s] online.";
595 push @targetsOut, @targets;
597 @targetsIn = (); # drop this list now.
598 notice
($user, @reply) if scalar(@reply);
599 if(scalar(@targetsOut)) {
600 return os_uinfo
($user, @targetsOut);
602 return $event::SUCCESS
;
605 sub os_svsnick
($$$) {
606 my ($user, $curnick, $newnick) = @_;
607 my $tuser = { NICK
=> $curnick };
609 if(!adminserv
::is_svsop
($user, adminserv
::S_ROOT
())) {
610 notice
($user, $err_deny);
611 return $event::SUCCESS
;
613 if ((!$curnick) or (!$newnick)) {
614 notice
($user, "Syntax: SVSNICK <curnick> <newnick>");
615 return $event::SUCCESS
;
617 if (!is_online
($tuser)) {
618 notice
($user, $curnick.' is not online.');
619 return $event::SUCCESS
;
621 if (nickserv
::is_online
($newnick)) {
622 notice
($user, $newnick.' already exists.');
623 return $event::SUCCESS
;
625 nickserv
::enforcer_quit
($newnick);
626 ircd
::svsnick
($osuser, $curnick, $newnick);
627 notice
($user, $curnick.' changed to '.$newnick);
628 return $event::SUCCESS
;
632 my ($user, @targets) = @_;
634 if(!adminserv
::can_do
($user, 'QLINE')) {
635 notice
($user, $err_deny);
636 return $event::SUCCESS
;
639 notice
($user, "Syntax: GNICK <nick>");
640 return $event::SUCCESS
;
642 foreach my $target (@targets) {
643 if (!is_online
($target)) {
644 notice
($user, $target.' is not online.');
647 my $newnick = nickserv
::collide
($target);
648 notice
($user, $target.' changed to '.$newnick);
650 return $event::SUCCESS
;
653 sub os_logonnews_pre
($$) {
654 my ($user, $type) = @_;
656 unless(adminserv
::is_svsop
($user, adminserv
::S_ADMIN
())) {
657 notice
($user, $err_deny);
661 return 'u' if($type =~ /^(user)|(u)$/i);
662 return 'o' if($type =~ /^(oper)|(o)$/i);
663 notice
($user, 'invalid LOGONNEWS <type>');
667 sub os_logonnews_add
($$$) {
668 my ($user, $type, $expiry, $msg) = @_;
670 return unless ($type = os_logonnews_pre
($user, $type));
672 my $mlength = length($msg);
673 if($mlength >= 350) {
674 notice
($user, 'Message is too long by '. $mlength-350 .' character(s). Maximum length is 350 chars');
679 $expiry = parse_time
($expiry);
685 my $src = get_user_nick
($user);
686 $count_logonnews->execute($type);
687 my $count = $count_logonnews->fetchrow_array;
689 $add_logonnews->execute($src, $expiry ? time()+$expiry : 0, $type, ++$count, $msg);
691 notice
($user, "Added new $newstypes{$type} News #\002$count\002");
694 sub os_logonnews_del
($$$) {
695 my ($user, $type, $id) = @_;
697 return unless ($type = os_logonnews_pre
($user, $type));
699 my $ret = $del_logonnews->execute($type, $id);
702 notice
($user, "News Item $newstypes{$type} News #\002$id\002 deleted");
703 $consolidate_logonnews->execute($type, $id);
706 notice
($user, "Delete of $newstypes{$type} News #\002$id\002 failed.",
707 "$newstypes{$type} #\002$id\002 does not exist?");
711 sub os_logonnews_list
($$) {
712 my ($user, $type) = @_;
714 return unless ($type = os_logonnews_pre
($user, $type));
717 push @reply, "\002$newstypes{$type}\002 News";
719 $list_logonnews->execute($type);
720 push @reply, "There is no $newstypes{$type} News"
721 unless($list_logonnews->rows);
722 while(my ($adder, $time, $expiry, $id, $msg) = $list_logonnews->fetchrow_array) {
723 my ($weeks, $days, $hours, $minutes, $seconds) = split_time
($expiry-time
());
724 my $expire_string = ($expiry?"Expires in ".($weeks?"$weeks weeks ":'').
725 ($days?"$days days ":'').
726 ($hours?"$hours hours ":'').
727 ($minutes?"$minutes minutes ":'')
729 push @reply, "$id\) $msg";
730 push @reply, join(' ', '', 'added: '.gmtime2
($time), $expire_string, "added by: $adder");
732 $list_logonnews->finish();
733 notice
($user, @reply);
736 sub os_except_pre
($) {
739 if (adminserv
::is_svsop
($user, adminserv
::S_ADMIN
()) ) {
743 notice
($user, $err_deny);
748 sub os_except_hostname_add
($$$) {
749 my ($user, $hostname, $limit) = @_;
751 os_except_pre
($user) or return 0;
753 if ($hostname =~ m/\@/ or not $hostname =~ /\./) {
754 notice
($user, 'Invalid hostmask.', 'A clone exception hostmask is the HOST portion only, no ident',
755 'and must contain at least one dot \'.\'');
759 $limit = MAX_LIM
() unless $limit;
761 my $src = get_user_nick
($user);
762 my $hostmask = $hostname;
763 $hostmask =~ s/\*/\%/g;
764 $add_clone_exceptname->execute($hostmask, $src, $limit);
765 notice
($user, "Clone exception for host \002$hostname\002 added.");
768 sub os_except_server_add
($$$) {
769 my ($user, $hostname, $limit) = @_;
771 os_except_pre
($user) or return 0;
773 if ($hostname =~ m/\@/ or not $hostname =~ /\./) {
774 notice
($user, 'Invalid hostmask.', 'A clone exception servername has no ident',
775 'and must contain at least one dot \'.\'');
779 $limit = MAX_LIM
() unless $limit;
781 my $src = get_user_nick
($user);
782 my $hostmask = $hostname;
783 $hostmask =~ s/\*/\%/g;
784 $add_clone_exceptserver->execute($hostmask, $src, $limit);
785 notice
($user, "Clone exception for server \002$hostname\002 added.");
788 sub os_except_IP_add
($$$$) {
789 my ($user, $IP, $limit) = @_;
791 os_except_pre
($user) or return 0;
794 ($IP, $mask) = split(/\//, $IP);
795 $mask = 32 unless $mask;
796 if ($IP =~ m/\@/ or not $IP =~ /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/) {
797 notice
($user, 'Invalid hostmask.', 'A clone exception IP has no ident',
798 'and must be a valid IP address with 4 octets (example: 1.2.3.4)');
802 $limit = MAX_LIM
() unless $limit;
804 my $src = get_user_nick
($user);
805 $add_clone_exceptip->execute($IP, $mask, $src, $limit);
806 notice
($user, "IP clone exception \002$IP\/$mask\002 added.");
809 sub os_except_hostname_del
($$) {
810 my ($user, $hostname) = @_;
812 os_except_pre
($user) or return 0;
814 my $hostmask = $hostname;
815 $hostmask =~ s/\*/\%/g;
816 my $ret = $del_clone_exceptname->execute($hostmask);
817 ircd
::notice
($osuser, main_conf_diag
, "hostname: $hostname; hostmask: $hostmask");
820 notice
($user, "\002$hostname\002 successfully deleted from the hostname exception list");
823 notice
($user, "Deletion of \002$hostname\002 \037failed\037. \002$hostname\002 entry does not exist?");
827 sub os_except_server_del
($$) {
828 my ($user, $hostname) = @_;
830 os_except_pre
($user) or return 0;
832 my $hostmask = $hostname;
833 $hostmask =~ s/\*/\%/g;
834 my $ret = $del_clone_exceptname->execute($hostmask);
837 notice
($user, "\002$hostname\002 successfully deleted from the server exception list");
840 notice
($user, "Deletion of \002$hostname\002 \037failed\037. \002$hostname\002 entry does not exist?");
844 sub os_except_IP_del
($$$) {
845 my ($user, $IP) = @_;
847 os_except_pre
($user) or return 0;
850 my ($IP, $mask) = split(/\//, $IP);
851 $mask = 32 unless $mask;
852 my $ret = $del_clone_exceptip->execute($IP);
855 notice
($user, "\002$IP/$mask\002 successfully deleted from the IP exception list");
858 notice
($user, "Deletion of \002$IP/$mask\002 \037failed\037. \002$IP/$mask\002 entry does not exist?");
862 sub os_except_list
($) {
866 $list_clone_exceptserver->execute();
867 while(my ($host, $adder, $lim) = $list_clone_exceptserver->fetchrow_array) {
869 push @data, ['Server:', $host, $lim!=MAX_LIM
()?$lim:'unlimited', "($adder)"];
872 $list_clone_exceptname->execute();
873 while(my ($host, $adder, $lim) = $list_clone_exceptname->fetchrow_array) {
875 push @data, ['Host:', $host, $lim!=MAX_LIM
()?$lim:'unlimited', "($adder)"];
878 $list_clone_exceptip->execute();
879 while(my ($ip, $mask, $adder, $lim) = $list_clone_exceptip->fetchrow_array) {
880 push @data, ['IP:', "$ip/$mask", $lim!=MAX_LIM
()?$lim:'unlimited', "($adder)"];
883 notice
($user, columnar
{TITLE
=> "Clone exception list:",
884 NOHIGHLIGHT
=> nr_chk_flag_user
($user, NRF_NOHIGHLIGHT
)}, @data);
887 sub os_session_list
($) {
888 my ($user, $lim) = @_;
891 notice
($user, "Please specify a number greater than 1.");
895 $get_session_list->execute($lim);
896 my $data = $get_session_list->fetchall_arrayref;
898 notice
($user, columnar
{TITLE
=> "Hosts with at least $lim sessions:",
899 NOHIGHLIGHT
=> nr_chk_flag_user
($user, NRF_NOHIGHLIGHT
)}, @$data);
902 sub os_chankill
($$$) {
903 my ($user, $cn, $reason) = @_;
905 unless(adminserv
::is_svsop
($user, adminserv
::S_OPER
())) {
906 notice
($user, $err_deny);
909 my $src = get_user_nick
($user);
911 chanserv
::chan_kill
({ CHAN
=> $cn }, "$reason ($src - ".gmtime2
(time()).")");
915 my ($user, $type) = @_;
917 unless (adminserv
::is_svsop
($user, adminserv
::S_ROOT
())) {
918 notice
($user, $err_deny);
919 return $event::SUCCESS
;
922 ircd
::rehash_all_servers
($type);
923 return $event::SUCCESS
;
926 sub os_svskill
($$$) {
927 my ($user, $targets, $reason) = @_;
930 if(!adminserv
::is_svsop
($user, adminserv
::S_ROOT
())) {
931 notice
($user, $err_deny);
932 return $event::SUCCESS
;
935 foreach my $target (split(',', $targets)) {
936 #my $tuser = { NICK => $target };
937 if (!is_online
({ NICK
=> $target })) {
938 notice
($user, $target.' is not online.');
939 return $event::SUCCESS
;
942 ircd
::svskill
($osuser, $target, $reason);
945 return $event::SUCCESS
;
949 my ($user, $targets, $reason) = @_;
952 if(!adminserv
::can_do
($user, 'KILL')) {
953 notice
($user, $err_deny);
954 return $event::SUCCESS
;
957 foreach my $target (split(',', $targets)) {
958 my $tuser = { NICK
=> $target, AGENT
=> $osuser };
959 if (!get_user_id
($tuser)) {
960 notice
($user, $target.' is not online.');
961 return $event::SUCCESS
;
964 nickserv
::kill_user
($tuser, "Killed by ".get_user_nick
($user).($reason ? ': '.$reason : ''));
970 my ($user, $zline, $target, @args) = @_;
973 return unless ($opernick = adminserv
::is_svsop
($user, adminserv
::S_OPER
));
976 $expiry = parse_time
(shift @args) if $args[0] =~ /^\+/;
977 my $reason = join(' ', @args);
978 $reason =~ s/^\:// if $reason;
980 if($target =~ /^-/) {
986 if($target =~ /\!/) {
987 notice
($user, "Invalid G:line target \002$target\002");
990 elsif($target =~ /^(\S+)\@(\S+)$/) {
991 ($ident, $host) = ($1, $2);
992 } elsif($target =~ /\./) {
993 ($ident, $host) = ('*', $target);
994 } elsif(valid_nick
($target)) {
995 my $tuser = { NICK
=> $target };
996 unless(get_user_id
($tuser)) {
997 notice
($user, "Unknown user \002$target\002");
1001 (undef, $host) = nickserv
::get_host
($tuser);
1004 $host = get_user_ip
($tuser);
1006 $host = get_ipv6_64
($host);
1010 notice
($user, "Invalid G:line target \002$target\002");
1015 ircd
::kline
(($osuser), $ident, $host, $expiry, $reason);
1017 ircd
::unkline
($opernick, $ident, $host);
1021 if($ident and $ident !~ /^\**$/) {
1022 notice
($user, "You cannot specify an ident in a Z:line");
1024 elsif ($host =~ /^(?:\d{1,3}\.){3}(?:\d{1,3})/) {
1025 # all is well, do nothing
1027 elsif ($host =~ /^[0-9\/\
*\?\
.]+$/) {
1028 # This may allow invalid CIDR, not sure.
1029 # We're trusting our opers to not do stupid things.
1030 # THIS MAY BE A SOURCE OF BUGS.
1032 # all is well, do nothing
1033 } elsif($host =~ /:/) {
1034 #validating IPv6 addrs without using inet_pton and inet_ntop is a crapshoot
1035 # for now, we do nothing.
1037 notice
($user, "Z:lines can only be placed on IPs or IP ranges");
1041 ircd
::zline
($osnick, $host, $expiry, $reason);
1043 ircd
::unzline
($opernick, $host);
1047 return $event::SUCCESS
;
1051 my ($user, @args) = @_;
1052 my $cmd = shift @args;
1054 if ($cmd =~ /(not?id|noidentify)/) {
1058 if (defined($args[0]) and $args[0] =~ /(not?id|noidentify)/) {
1063 return __os_massmod
($user, uc 'clones', $cmd, \
&chanserv
::get_users_nochans
, $noid, @args);
1066 my ($user, @args) = @_;
1067 my $cmd = shift @args;
1068 my $target = shift @args;
1070 return __os_massmod
($user, 'Clones', $cmd, \
&get_clones
, $target, @args);
1073 sub os_killnew
($@) {
1074 my ($user, @args) = @_;
1075 my $cmd = shift @args;
1078 if ($cmd =~ /(not?id|noidentify)/) {
1082 if (defined($args[0]) and $args[0] =~ /(not?id|noidentify)/) {
1086 if(defined($args[0] and $args[0] =~ /^\+/)) {
1087 $time = parse_time
(shift @args);
1090 return __os_massmod
($user, 'killnew', $cmd, \
&get_newusers
, [$noid, $time], @args);
1093 sub __os_massmod
($$$$@) {
1094 my ($user, $cmd0, $cmd1, $func, $arg, @args) = @_;
1095 my $msg = join(' ', @args);
1097 if($cmd1 =~ /^list$/i) {
1100 foreach my $tuser (&$func($arg)) {
1101 push @data, [get_user_nick
($tuser), (is_online
($tuser) ? "\002Online\002" : "\002Offline\002")];
1104 if($cmd0 eq 'Clones') {
1105 $title = "$cmd0 matching \002$arg\002";
1106 } elsif($cmd0 eq 'Loners') {
1107 $title = "$cmd0 ".($arg ? 'Not identified' : '');
1108 } elsif($cmd0 eq 'Loners') {
1109 $title = "New users ".($arg ? 'Not identified' : '');
1111 notice
($user, columnar
{TITLE
=> $title,
1112 NOHIGHLIGHT
=> nr_chk_flag_user
($user, NRF_NOHIGHLIGHT
)}, @data);
1114 elsif($cmd1 =~ /^uinfo$/i) {
1115 notice
($user, get_uinfo
($user, &$func($arg)));
1117 elsif($cmd1 =~ /^kill$/i) {
1118 unless(adminserv
::can_do
($user, 'KILL')) {
1119 notice
($user, $err_deny);
1122 foreach my $tuser (&$func($arg)) {
1123 next unless is_online
($tuser);
1124 $tuser->{AGENT
} = $osuser;
1125 nickserv
::kill_user
($tuser,
1126 "Killed by \002".get_user_nick
($user)."\002".
1127 ($msg ? ": $msg" : '')
1131 elsif($cmd1 =~ /^kline$/i) {
1132 unless(adminserv
::is_svsop
($user, adminserv
::S_OPER
())) {
1133 notice
($user, $err_deny);
1136 foreach my $tuser (&$func($arg)) {
1137 next unless is_online
($tuser);
1138 $tuser->{AGENT
} = $osuser;
1139 nickserv
::kline_user
($tuser, services_conf_chankilltime
,
1140 "K:Lined by \002".get_user_nick
($user)."\002".
1141 ($msg ? ": $msg" : '')
1145 elsif($cmd1 =~ /^(msg|message|notice)$/i) {
1146 notice
($user, "Must have message to send") unless(@args);
1147 foreach my $tuser (&$func($arg)) {
1148 next unless is_online
($tuser);
1149 $tuser->{AGENT
} = $osuser;
1151 "Automated message from \002".get_user_nick
($user),
1156 elsif($cmd1 =~ /^fjoin$/i) {
1157 unless(adminserv
::can_do
($user, 'FJOIN')) {
1158 notice
($user, $err_deny);
1162 if ($args[0] !~ /^#/) {
1163 notice
($user, "\002".$args[0]."\002 is not a valid channel name");
1167 foreach my $tuser (&$func($arg)) {
1168 next unless is_online
($tuser);
1169 my $cn = $msg; # not a message, most cases it is
1170 $tuser->{AGENT
} = $osuser;
1171 ircd
::svsjoin
($osuser, get_user_nick
($tuser), $args[0]);
1175 notice
($user, "Unknown $cmd0 command: $cmd1",
1176 "Syntax: OS $cmd0 [LIST|UINFO|MSG|FJOIN|KILL|KLINE] [msg/reason]");
1183 my ($nick, $type) = @_;
1185 my ($banner, @reply);
1188 $banner = "\002Logon News\002";
1190 elsif ($type eq 'o') {
1191 $banner = "\002Oper News\002";
1193 $get_logonnews->execute($type);
1194 while(my ($adder, $time, $msg) = $get_logonnews->fetchrow_array) {
1195 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($time);
1197 push @reply, "[$banner ".$months[$mon]." $mday $year] $msg";
1199 $get_logonnews->finish();
1200 ircd
::notice
(main_conf_local
, $nick, @reply) if scalar(@reply);
1204 my ($user, $perm) = @_;
1206 if(adminserv
::can_do
($user, $perm)) {
1210 notice
($user, $err_deny);
1215 add_timer
('OperServ Expire', 60, __PACKAGE__
, 'operserv::expire');
1217 $get_expired_qlines->execute();
1218 while (my ($mask) = $get_expired_qlines->fetchrow_array() ) {
1219 ircd
::unsqline
($mask);
1220 $del_qline->execute($mask);
1222 $get_expired_qlines->finish();
1224 #don't run this code yet.
1226 $get_expired_akills->execute();
1227 while (my ($mask) = $get_expired_akills->fetchrow_array() ) {
1228 ($ident, $host) = split('@', $mask);
1229 ircd
::unkline
($osnick, $ident, $host);
1230 $del_akill->execute($mask);
1232 $get_expired_akills->finish();
1235 $del_expired_logonnews->execute();
1239 my ($user, @userlist) = @_;
1241 foreach my $tuser (@userlist) {
1242 my ($ident, $host, $vhost, $gecos, $server, $signontime, $quittime) = get_user_info
($tuser);
1243 my $modes = nickserv
::get_user_modes
($tuser);
1244 my $target = get_user_nick
($tuser);
1246 my ($curchans, $oldchans) = chanserv
::get_user_chans_recent
($tuser);
1249 ["Status:", (nickserv
::is_online
($tuser) ?
1250 "Online (".gmtime2
($signontime).')' :
1251 "Offline (".gmtime2
($quittime).')'
1254 ["ID Nicks:", join(', ', nickserv
::get_id_nicks
($tuser))],
1255 ["Channels:", join(', ', @$curchans)],
1256 ["Recently Parted:", join(', ', @$oldchans)],
1257 ["Flood level:", get_flood_level
($tuser)],
1258 ["Hostmask:", "$target\!$ident\@$vhost"],
1260 ["Connecting from:", "$host"],
1261 ["Current Server:", $server],
1264 if(module
::is_loaded
('country')) {
1265 push @data, ["Country:", country
::get_user_country_long
($tuser)];
1266 } elsif(module
::is_loaded
('geoip')) {
1267 push @data, ["Location:", geoip
::stringify_location
(geoip
::get_user_location
($tuser))];
1270 push @reply, columnar
{TITLE
=> "User info for \002$target\002:",
1271 NOHIGHLIGHT
=> nr_chk_flag_user
($user, NRF_NOHIGHLIGHT
)}, @data;
1279 foreach my $target (split(',', $targets)) {
1280 my $sth; # statement handle. You'll see what I'll do with it next!
1281 if($target =~ /^(?:\d{1,3}\.){3}\d{1,3}$/) {
1282 $sth = $get_clones_fromipv4;
1283 } elsif($target =~ /\./) { # doesn't really work with localhost. oh well.
1284 $sth = $get_clones_fromhost;
1286 $sth = $get_clones_fromnick;
1289 $sth->execute($target);
1290 while(my ($nick, $id, $online) = $sth->fetchrow_array()) {
1291 push @users, { NICK
=> $nick, ID
=> $id, ONLINE
=> $online };
1298 sub get_newusers
($) {
1299 my ($noid, $time) = @{$_[0]};
1300 ircd
::debug
("get_newusers: $time");
1302 my $sth; # statement handle. You'll see what I'll do with it next!
1304 $sth = $get_newusers_noid;
1306 $sth = $get_newusers;
1309 $sth->execute(CORE
::time()-$time);
1310 while(my ($nick, $id, $online) = $sth->fetchrow_array()) {
1311 push @users, { NICK
=> $nick, ID
=> $id, ONLINE
=> $online };