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 :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';
44 *kill_user
= \
&nickserv
::kill_user
;
46 our $osnick_default = 'OperServ';
47 our $osnick = $osnick_default;
55 $add_akill, $del_akill, $get_all_akills, $get_expired_akills,
56 $get_akill, $check_akill,
60 $add_qline, $del_qline, $get_all_qlines, $get_expired_qlines,
61 $get_qline, $check_qline,
63 $add_logonnews, $del_logonnews, $list_logonnews, $get_logonnews,
64 $consolidate_logonnews, $count_logonnews, $del_expired_logonnews,
66 $add_clone_exceptname, $add_clone_exceptserver, $add_clone_exceptip,
67 $del_clone_exceptname, $del_clone_exceptip,
68 $list_clone_exceptname, $list_clone_exceptserver, $list_clone_exceptip,
70 $get_clones_fromhost, $get_clones_fromnick, $get_clones_fromid, $get_clones_fromipv4,
77 $add_akill = $dbh->prepare("INSERT INTO akill SET setter=?, mask=?, reason=?, time=?, expire=?");
78 $del_akill = $dbh->prepare("DELETE FROM akill WHERE mask=?");
79 $get_all_akills = $dbh->prepare("SELECT setter, mask, reason, time, expire FROM akill ORDER BY time ASC");
80 $get_akill = $dbh->prepare("SELECT setter, mask, reason, time, expire FROM akill WHERE mask=?");
81 $check_akill = $dbh->prepare("SELECT 1 FROM akill WHERE mask=?");
83 $get_expired_akills = $dbh->prepare("SELECT setter, mask, reason, time, expire FROM akill WHERE expire < UNIX_TIMESTAMP() AND expire!=0");
86 $add_qline = $dbh->prepare("INSERT INTO qline SET setter=?, mask=?, reason=?, time=?, expire=?");
87 $del_qline = $dbh->prepare("DELETE FROM qline WHERE mask=?");
88 $get_all_qlines = $dbh->prepare("SELECT setter, mask, reason, time, expire FROM qline ORDER BY time ASC");
89 $get_qline = $dbh->prepare("SELECT setter, mask, reason, time, expire FROM qline WHERE mask=?");
90 $check_qline = $dbh->prepare("SELECT 1 FROM qline WHERE mask=?");
92 $get_expired_qlines = $dbh->prepare("SELECT mask FROM qline WHERE expire < UNIX_TIMESTAMP() AND expire!=0");
94 $add_logonnews = $dbh->prepare("INSERT INTO logonnews SET setter=?, expire=?, type=?, id=?, msg=?, time=UNIX_TIMESTAMP()");
95 $del_logonnews = $dbh->prepare("DELETE FROM logonnews WHERE type=? AND id=?");
96 $list_logonnews = $dbh->prepare("SELECT setter, time, expire, id, msg FROM logonnews WHERE type=? ORDER BY id ASC");
97 $get_logonnews = $dbh->prepare("SELECT setter, time, msg FROM logonnews WHERE type=? ORDER BY id ASC");
98 $consolidate_logonnews = $dbh->prepare("UPDATE logonnews SET id=id-1 WHERE type=? AND id>?");
99 $count_logonnews = $dbh->prepare("SELECT COUNT(*) FROM logonnews WHERE type=?");
100 $del_expired_logonnews = $dbh->prepare("DELETE FROM logonnews WHERE expire < UNIX_TIMESTAMP() AND expire!=0");
102 $add_clone_exceptname = $dbh->prepare("REPLACE INTO sesexname SET host=?, serv=0, adder=?, lim=?");
103 $add_clone_exceptserver = $dbh->prepare("REPLACE INTO sesexname SET host=?, serv=1, adder=?, lim=?");
104 $add_clone_exceptip = $dbh->prepare("REPLACE INTO sesexip SET ip=INET_ATON(?), mask=?, adder=?, lim=?");
106 $del_clone_exceptname = $dbh->prepare("DELETE FROM sesexname WHERE host=?");
107 $del_clone_exceptip = $dbh->prepare("DELETE FROM sesexip WHERE ip=INET_ATON(?)");
109 $list_clone_exceptname = $dbh->prepare("SELECT host, adder, lim FROM sesexname WHERE serv=0 ORDER BY host ASC");
110 $list_clone_exceptserver = $dbh->prepare("SELECT host, adder, lim FROM sesexname WHERE serv=1 ORDER BY host ASC");
111 $list_clone_exceptip = $dbh->prepare("SELECT INET_NTOA(ip), mask, adder, lim FROM sesexip ORDER BY ip ASC");
113 $get_clones_fromhost = $dbh->prepare("SELECT user.nick, user.id, user.online
114 FROM user JOIN user AS clone ON (user.ip=clone.ip)
115 WHERE clone.host=? GROUP BY id");
116 $get_clones_fromnick = $dbh->prepare("SELECT user.nick, user.id, user.online
117 FROM user JOIN user AS clone ON (user.ip=clone.ip)
118 WHERE clone.nick=? GROUP BY id");
119 $get_clones_fromid = $dbh->prepare("SELECT user.nick, user.id, user.online
120 FROM user JOIN user AS clone ON (user.ip=clone.ip)
121 WHERE clone.id=? GROUP BY id");
122 $get_clones_fromipv4 = $dbh->prepare("SELECT user.nick, user.id, user.online
123 FROM user JOIN user AS clone ON (user.ip=clone.ip)
124 WHERE clone.ip=INET_ATON(?) GROUP BY id");
126 $get_session_list = $dbh->prepare("SELECT host, COUNT(*) AS c FROM user WHERE online=1 GROUP BY host HAVING c >= ?");
130 my ($src, $dst, $msg) = @_;
132 my @args = split(/\s+/, $msg);
133 my $cmd = shift @args;
135 my $user = { NICK
=> $src, AGENT
=> $dst };
137 services
::ulog
($osnick, LOG_INFO
(), "cmd: [$msg]", $user);
139 return if flood_check
($user);
140 unless(adminserv
::is_svsop
($user) or adminserv
::is_ircop
($user)) {
141 notice
($user, $err_deny);
142 ircd
::globops
($osnick, "\002$src\002 failed access to $osnick $msg");
146 if ($cmd =~ /^fjoin$/i) { os_fjoin
($user, @args); }
147 elsif ($cmd =~ /^fpart$/i) { os_fpart
($user, @args); }
148 elsif ($cmd =~ /^unidentify$/i) { os_unidentify
($user, @args); }
149 elsif ($cmd =~ /^qline$/i) {
150 my $cmd2 = shift @args;
152 if($cmd2 =~ /^add$/i) {
153 if(@args >= 3 and $args[0] =~ /^\+/) {
154 @args = split(/\s+/, $msg, 5);
156 os_qline_add
($user, $args[2], $args[3], $args[4]);
159 @args = split(/\s+/, $msg, 4);
161 os_qline_add
($user, 0, $args[2], $args[3]);
164 notice
($user, 'Syntax: QLINE ADD [+expiry] <mask> <reason>');
167 elsif($cmd2 =~ /^del$/i) {
169 os_qline_del
($user, $args[0]);
172 notice
($user, 'Syntax: QLINE DEL <mask>');
175 elsif($cmd2 =~ /^list$/i) {
177 os_qline_list
($user);
180 notice
($user, 'Syntax: QLINE LIST');
184 elsif ($cmd =~ /^jupe$/i) {
186 os_jupe
($user, shift @args, join(' ', @args));
189 notice
($user, 'Syntax: JUPE <server> <reason>');
192 elsif ($cmd =~ /^uinfo$/i) { os_uinfo
($user, @args); }
193 elsif ($cmd =~ /^ninfo$/i) { os_ninfo
($user, @args); }
194 elsif ($cmd =~ /^svsnick$/i) { os_svsnick
($user, $args[0], $args[1]); }
195 elsif ($cmd =~ /^gnick$/i) { os_gnick
($user, @args); }
196 elsif ($cmd =~ /^help$/i) { sendhelp
($user, 'operserv', @args) }
197 elsif ($cmd =~ /^(staff|listadm)$/i) { adminserv
::as_staff
($user) }
198 elsif ($cmd =~ /^logonnews$/i) {
199 my $cmd2 = shift @args;
201 if($cmd2 =~ /^add$/i) {
202 if(@args >= 3 and $args[1] =~ /^\+/) {
203 @args = split(/\s+/, $msg, 5);
205 os_logonnews_add
($user, $args[2], $args[3], $args[4]);
208 @args = split(/\s+/, $msg, 4);
210 os_logonnews_add
($user, $args[2], 0, $args[3]);
213 notice
($user, 'Syntax: LOGONNEWS ADD <type> [+expiry] <reason>');
216 elsif($cmd2 =~ /^del$/i) {
218 os_logonnews_del
($user, $args[0], $args[1]);
221 notice
($user, 'Syntax: LOGONNEWS DEL <type> <id>');
224 elsif($cmd2 =~ /^list$/i) {
226 os_logonnews_list
($user, $args[0]);
229 notice
($user, 'Syntax: LOGONNEWS LIST <type>');
233 notice
($user, 'Syntax: LOGONNEWS <LIST|ADD|DEL> <type>');
236 elsif($cmd =~ /^except(ion)?$/i) {
237 my $cmd2 = shift @args;
238 if($cmd2 =~ /^server$/i) {
239 my $cmd3 = shift @args;
240 if($cmd3 =~ /^a(dd)?$/) {
242 os_except_server_add
($user, $args[0], $args[1]);
245 notice
($user, 'Syntax EXCEPT SERVER ADD <hostname> <limit>');
248 elsif($cmd =~ /^d(el)?$/) {
250 os_except_server_del
($user, $args[0]);
253 notice
($user, 'Syntax EXCEPT SERVER DEL <hostname>');
257 notice
($user, 'Syntax EXCEPT SERVER <ADD|DEL>');
260 elsif($cmd2 =~ /^h(ostname)?$/i) {
261 my $cmd3 = shift @args;
262 if($cmd3 =~ /^a(dd)?$/) {
264 os_except_hostname_add
($user, $args[0], $args[1]);
267 notice
($user, 'Syntax EXCEPT HOSTNAME ADD <hostname> <limit>');
270 elsif($cmd3 =~ /^d(el)?$/) {
272 os_except_hostname_del
($user, $args[0]);
275 notice
($user, 'Syntax EXCEPT HOSTNAME DEL <hostname>');
279 notice
($user, 'Syntax EXCEPT HOSTNAME <ADD|DEL>');
282 elsif($cmd2 =~ /^i(p)?$/i) {
283 my $cmd3 = shift @args;
284 if($cmd3 =~ /^a(dd)?$/) {
286 os_except_IP_add
($user, $args[0], $args[1]);
289 notice
($user, 'Syntax EXCEPT IP ADD <IP/mask> <limit>');
292 elsif($cmd3 =~ /^d(el)?$/) {
294 os_except_IP_del
($user, $args[0]);
297 notice
($user, 'Syntax EXCEPT IP DEL <IP>');
301 notice
($user, 'Syntax EXCEPT IP <ADD|DEL>');
304 elsif($cmd2 =~ /^l(ist)?$/i) {
306 os_except_list
($user);
309 notice
($user, 'Syntax EXCEPT LIST');
313 notice
($user, 'Syntax: EXCEPT <SERVER|HOSTNAME|IP|LIST>');
316 elsif($cmd =~ /^session$/i) {
318 os_session_list
($user, $args[0]);
320 notice
($user, 'Syntax SESSION <lim>');
323 elsif($cmd =~ /^chankill$/i) {
325 (undef, @args) = split(/\s+/, $msg, 3);
326 os_chankill
($user, @args);
328 notice
($user, 'Syntax: CHANKILL <#chan> <reason>');
331 elsif ($cmd =~ /^rehash$/i) {
333 os_rehash
($user, @args);
336 notice
($user, 'Syntax: REHASH [type]');
339 elsif ($cmd =~ /^loners$/i) {
340 os_loners
($user, @args);
342 elsif($cmd =~ /^svskill$/i) {
344 os_svskill
($user, shift @args, join(' ', @args));
347 notice
($user, 'Syntax SVSKILL <target> <reason here>');
350 elsif($cmd =~ /^kill$/i) {
352 os_kill
($user, shift @args, join(' ', @args));
355 notice
($user, 'Syntax KILL <target> <reason here>');
358 elsif ($cmd =~ /^clones$/i) {
359 os_clones
($user, @args);
361 elsif ($cmd =~ /^m(ass)?kill$/i) {
362 os_clones
($user, 'KILL', @args);
364 elsif($cmd =~ /^(kline|gline)$/i) {
366 os_gline
($user, 0, @args);
369 notice
($user, 'Syntax GLINE <target> [+time] [reason here]');
372 elsif($cmd =~ /^(zline|gzline)$/i) {
374 os_gline
($user, 1, @args);
377 notice
($user, 'Syntax GZLINE <target> [+time] [reason here]');
381 else { notice
($user, "Unknown command."); }
385 my ($user, $target, @chans) = @_;
386 if ((!$target or !@chans) or !($chans[0] =~ /^#/)) {
387 notice
($user, "Syntax: /OS FJOIN <nick> <#channel1> [#channel2]");
389 unless (is_online
($target)) {
390 notice
($user, "\002$target\002 is not online");
394 if (!adminserv
::can_do
($user, 'FJOIN')) {
395 notice
($user, "You don't have the right access");
396 return $event::SUCCESS
;
398 ircd
::svsjoin
($osnick, $target, @chans);
402 my ($user, $target, @params) = @_;
403 if ((!$target or !@params) or !($params[0] =~ /^#/)) {
404 notice
($user, "Syntax: /OS FPART <nick> <#channel1> [#channel2] [reason]");
406 unless (is_online
($target)) {
407 notice
($user, "\002$target\002 is not online");
411 if (!adminserv
::can_do
($user, 'FJOIN')) {
412 notice
($user, "You don't have the right access");
413 return $event::SUCCESS
;
416 my ($reason, @chans);
417 while ($params[0] =~ /^#/) {
418 push @chans, shift @params;
420 $reason = join(' ', @params) if @params;
422 ircd
::svspart
($osnick, $target, $reason, @chans);
425 sub os_qline_add
($$$$) {
426 my ($user, $expiry, $mask, $reason) = @_;
428 chk_auth
($user, 'QLINE') or return;
430 $expiry = parse_time
($expiry);
431 if($expiry) { $expiry += time() }
434 $check_qline->execute($mask);
435 if ($check_qline->fetchrow_array) {
436 notice
($user, "$mask is already qlined");
437 return $event::SUCCESS
;
439 my $src = get_user_nick
($user);
440 $add_qline->execute($src, $mask, $reason, time(), $expiry);
441 ircd
::sqline
($mask, $reason);
442 notice
($user, "$mask is now Q:lined");
446 sub os_qline_del
($$) {
447 my($user, $mask) = @_;
449 chk_auth
($user, 'QLINE') or return;
451 $check_qline->execute($mask);
452 if($check_qline->fetchrow_array) {
453 $del_qline->execute($mask);
454 ircd
::unsqline
($mask);
455 notice
($user, "$mask unqlined");
457 notice
($user, "$mask is not qlined");
461 sub os_qline_list
($) {
465 chk_auth
($user, 'QLINE') or return;
467 push @reply, 'Q:line list:';
469 $get_all_qlines->execute();
471 while (my ($setter, $mask, $reason, $time, $expiry) = $get_all_qlines->fetchrow_array) {
473 my $akill_entry1 = " $i) \002$mask\002 $reason";
474 my $akill_entry2 = " set by $setter on ".gmtime2
($time).'; ';
476 my ($weeks, $days, $hours, $minutes, $seconds) = split_time
($expiry-time
());
477 $akill_entry2 .= "Expires in ".($weeks?"$weeks weeks ":'').
478 ($days?"$days days ":'').
479 ($hours?"$hours hours ":'').
480 ($minutes?"$minutes minutes ":'');
483 $akill_entry2 .= "Does not expire.";
485 push @reply, $akill_entry1; push @reply, $akill_entry2;
487 $get_all_qlines->finish();
490 notice
($user, @reply) if @reply;
494 # introduces fake server to network.
495 my ($user, $server, $reason) = @_;
497 unless (adminserv
::is_svsop
($user, adminserv
::S_ROOT
())) {
498 notice
($user, $err_deny);
499 return $event::SUCCESS
;
501 unless (valid_server
($server)) {
502 notice
($user, "$server is not a valid servername.");
503 return $event::SUCCESS
;
505 if (get_server_state
($server)) {
506 notice
($user, "$server is currently connected. You must SQUIT before using JUPE.");
507 return $event::SUCCESS
;
510 ircd
::jupe_server
($server, "Juped by ".get_user_nick
($user).": $reason");
511 notice
($user, "$server is now juped.");
512 return $event::SUCCESS
;
515 sub os_unidentify
($$) {
516 my ($user, $tnick) = @_;
518 my $tuser = { NICK
=> $tnick };
521 unless ($tuid = get_user_id
($tuser)) {
522 notice
($user, "\002$tnick\002 is not online");
524 unless (adminserv
::can_do
($user, 'SERVOP')) {
525 notice
($user, $err_deny);
527 $nickserv::logout-
>execute($tuid);
528 notice
($user, "$tnick logged out from all nick identifies");
532 my ($user, @targets) = @_;
535 foreach my $target (@targets) {
536 if($target =~ /\,/) {
537 push @targets, split(',', $target);
541 my $tuser = { NICK
=> $target };
542 my $tuid = get_user_id
($tuser);
544 notice
($user, "\002$target\002: user not found");
547 push @userlist, $tuser;
550 notice
($user, get_uinfo
($user, @userlist));
551 return $event::SUCCESS
;
555 my ($user, @targetsIn) = @_;
558 foreach my $target (@targetsIn) {
559 if(not nickserv
::is_registered
($target)) {
560 notice
($user, "\002$target\002: is not registered.");
562 my @targets = nickserv
::get_nick_user_nicks
($target);
563 if(scalar(@targets) == 0) {
564 notice
($user, "\002$target\002: no user[s] online.");
567 push @targetsOut, @targets;
569 if(scalar(@targetsOut)) {
570 return os_uinfo
($user, @targetsOut);
572 return $event::SUCCESS
;
575 sub os_svsnick
($$$) {
576 my ($user, $curnick, $newnick) = @_;
577 my $tuser = { NICK
=> $curnick };
579 if(!adminserv
::is_svsop
($user, adminserv
::S_ROOT
())) {
580 notice
($user, $err_deny);
581 return $event::SUCCESS
;
583 if ((!$curnick) or (!$newnick)) {
584 notice
($user, "Syntax: SVSNICK <curnick> <newnick>");
585 return $event::SUCCESS
;
587 if (!is_online
($tuser)) {
588 notice
($user, $curnick.' is not online.');
589 return $event::SUCCESS
;
591 if (nickserv
::is_online
($newnick)) {
592 notice
($user, $newnick.' already exists.');
593 return $event::SUCCESS
;
595 nickserv
::enforcer_quit
($newnick);
596 ircd
::svsnick
($osnick, $curnick, $newnick);
597 notice
($user, $curnick.' changed to '.$newnick);
598 return $event::SUCCESS
;
602 my ($user, @targets) = @_;
604 if(!adminserv
::can_do
($user, 'QLINE')) {
605 notice
($user, $err_deny);
606 return $event::SUCCESS
;
609 notice
($user, "Syntax: GNICK <nick>");
610 return $event::SUCCESS
;
612 foreach my $target (@targets) {
613 if (!is_online
($target)) {
614 notice
($user, $target.' is not online.');
617 my $newnick = nickserv
::collide
($target);
618 notice
($user, $target.' changed to '.$newnick);
620 return $event::SUCCESS
;
623 sub os_logonnews_pre
($$) {
624 my ($user, $type) = @_;
626 unless(adminserv
::is_svsop
($user, adminserv
::S_ADMIN
())) {
627 notice
($user, $err_deny);
631 return 'u' if($type =~ /^(user)|(u)$/i);
632 return 'o' if($type =~ /^(oper)|(o)$/i);
633 notice
($user, 'invalid LOGONNEWS <type>');
637 sub os_logonnews_add
($$$) {
638 my ($user, $type, $expiry, $msg) = @_;
640 return unless ($type = os_logonnews_pre
($user, $type));
642 my $mlength = length($msg);
643 if($mlength >= 350) {
644 notice
($user, 'Message is too long by '. $mlength-350 .' character(s). Maximum length is 350 chars');
649 $expiry = parse_time
($expiry);
655 my $src = get_user_nick
($user);
656 $count_logonnews->execute($type);
657 my $count = $count_logonnews->fetchrow_array;
659 $add_logonnews->execute($src, $expiry ? time()+$expiry : 0, $type, ++$count, $msg);
661 notice
($user, "Added new $newstypes{$type} News #\002$count\002");
664 sub os_logonnews_del
($$$) {
665 my ($user, $type, $id) = @_;
667 return unless ($type = os_logonnews_pre
($user, $type));
669 my $ret = $del_logonnews->execute($type, $id);
672 notice
($user, "News Item $newstypes{$type} News #\002$id\002 deleted");
673 $consolidate_logonnews->execute($type, $id);
676 notice
($user, "Delete of $newstypes{$type} News #\002$id\002 failed.",
677 "$newstypes{$type} #\002$id\002 does not exist?");
681 sub os_logonnews_list
($$) {
682 my ($user, $type) = @_;
684 return unless ($type = os_logonnews_pre
($user, $type));
687 push @reply, "\002$newstypes{$type}\002 News";
689 $list_logonnews->execute($type);
690 push @reply, "There is no $newstypes{$type} News"
691 unless($list_logonnews->rows);
692 while(my ($adder, $time, $expiry, $id, $msg) = $list_logonnews->fetchrow_array) {
693 my ($weeks, $days, $hours, $minutes, $seconds) = split_time
($expiry-time
());
694 my $expire_string = ($expiry?"Expires in ".($weeks?"$weeks weeks ":'').
695 ($days?"$days days ":'').
696 ($hours?"$hours hours ":'').
697 ($minutes?"$minutes minutes ":'')
699 push @reply, "$id\) $msg";
700 push @reply, join(' ', '', 'added: '.gmtime2
($time), $expire_string, "added by: $adder");
702 $list_logonnews->finish();
703 notice
($user, @reply);
706 sub os_except_pre
($) {
709 if (adminserv
::is_svsop
($user, adminserv
::S_ADMIN
()) ) {
713 notice
($user, $err_deny);
718 sub os_except_hostname_add
($$$) {
719 my ($user, $hostname, $limit) = @_;
721 os_except_pre
($user) or return 0;
723 if ($hostname =~ m/\@/ or not $hostname =~ /\./) {
724 notice
($user, 'Invalid hostmask.', 'A clone exception hostmask is the HOST portion only, no ident',
725 'and must contain at least one dot \'.\'');
729 $limit = MAX_LIM
() unless $limit;
731 my $src = get_user_nick
($user);
732 my $hostmask = $hostname;
733 $hostmask =~ s/\*/\%/g;
734 $add_clone_exceptname->execute($hostmask, $src, $limit);
735 notice
($user, "Clone exception for host \002$hostname\002 added.");
738 sub os_except_server_add
($$$) {
739 my ($user, $hostname, $limit) = @_;
741 os_except_pre
($user) or return 0;
743 if ($hostname =~ m/\@/ or not $hostname =~ /\./) {
744 notice
($user, 'Invalid hostmask.', 'A clone exception servername has no ident',
745 'and must contain at least one dot \'.\'');
749 $limit = MAX_LIM
() unless $limit;
751 my $src = get_user_nick
($user);
752 my $hostmask = $hostname;
753 $hostmask =~ s/\*/\%/g;
754 $add_clone_exceptserver->execute($hostmask, $src, $limit);
755 notice
($user, "Clone exception for server \002$hostname\002 added.");
758 sub os_except_IP_add
($$$$) {
759 my ($user, $IP, $limit) = @_;
761 os_except_pre
($user) or return 0;
764 ($IP, $mask) = split(/\//, $IP);
765 $mask = 32 unless $mask;
766 if ($IP =~ m/\@/ or not $IP =~ /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/) {
767 notice
($user, 'Invalid hostmask.', 'A clone exception IP has no ident',
768 'and must be a valid IP address with 4 octets (example: 1.2.3.4)');
772 $limit = MAX_LIM
() unless $limit;
774 my $src = get_user_nick
($user);
775 $add_clone_exceptip->execute($IP, $mask, $src, $limit);
776 notice
($user, "IP clone exception \002$IP\/$mask\002 added.");
779 sub os_except_hostname_del
($$) {
780 my ($user, $hostname) = @_;
782 os_except_pre
($user) or return 0;
784 my $hostmask = $hostname;
785 $hostmask =~ s/\*/\%/g;
786 my $ret = $del_clone_exceptname->execute($hostmask);
787 ircd
::notice
($osnick, main_conf_diag
, "hostname: $hostname; hostmask: $hostmask");
790 notice
($user, "\002$hostname\002 successfully deleted from the hostname exception list");
793 notice
($user, "Deletion of \002$hostname\002 \037failed\037. \002$hostname\002 entry does not exist?");
797 sub os_except_server_del
($$) {
798 my ($user, $hostname) = @_;
800 os_except_pre
($user) or return 0;
802 my $hostmask = $hostname;
803 $hostmask =~ s/\*/\%/g;
804 my $ret = $del_clone_exceptname->execute($hostmask);
807 notice
($user, "\002$hostname\002 successfully deleted from the server exception list");
810 notice
($user, "Deletion of \002$hostname\002 \037failed\037. \002$hostname\002 entry does not exist?");
814 sub os_except_IP_del
($$$) {
815 my ($user, $IP) = @_;
817 os_except_pre
($user) or return 0;
820 my ($IP, $mask) = split(/\//, $IP);
821 $mask = 32 unless $mask;
822 my $ret = $del_clone_exceptip->execute($IP);
825 notice
($user, "\002$IP/$mask\002 successfully deleted from the IP exception list");
828 notice
($user, "Deletion of \002$IP/$mask\002 \037failed\037. \002$IP/$mask\002 entry does not exist?");
832 sub os_except_list
($) {
836 $list_clone_exceptserver->execute();
837 while(my ($host, $adder, $lim) = $list_clone_exceptserver->fetchrow_array) {
839 push @data, ['Server:', $host, $lim!=MAX_LIM
()?$lim:'unlimited', "($adder)"];
842 $list_clone_exceptname->execute();
843 while(my ($host, $adder, $lim) = $list_clone_exceptname->fetchrow_array) {
845 push @data, ['Host:', $host, $lim!=MAX_LIM
()?$lim:'unlimited', "($adder)"];
848 $list_clone_exceptip->execute();
849 while(my ($ip, $mask, $adder, $lim) = $list_clone_exceptip->fetchrow_array) {
850 push @data, ['IP:', "$ip/$mask", $lim!=MAX_LIM
()?$lim:'unlimited', "($adder)"];
853 notice
($user, columnar
{TITLE
=> "Clone exception list:",
854 NOHIGHLIGHT
=> nr_chk_flag_user
($user, NRF_NOHIGHLIGHT
)}, @data);
857 sub os_session_list
($) {
858 my ($user, $lim) = @_;
861 notice
($user, "Please specify a number greater than 1.");
865 $get_session_list->execute($lim);
866 my $data = $get_session_list->fetchall_arrayref;
868 notice
($user, columnar
{TITLE
=> "Hosts with at least $lim sessions:",
869 NOHIGHLIGHT
=> nr_chk_flag_user
($user, NRF_NOHIGHLIGHT
)}, @$data);
872 sub os_chankill
($$$) {
873 my ($user, $cn, $reason) = @_;
875 unless(adminserv
::is_svsop
($user, adminserv
::S_OPER
())) {
876 notice
($user, $err_deny);
879 my $src = get_user_nick
($user);
881 chanserv
::chan_kill
({ CHAN
=> $cn }, "$reason ($src - ".gmtime2
(time()).")");
885 my ($user, $type) = @_;
887 unless (adminserv
::is_svsop
($user, adminserv
::S_ROOT
())) {
888 notice
($user, $err_deny);
889 return $event::SUCCESS
;
892 ircd
::rehash_all_servers
($type);
893 return $event::SUCCESS
;
897 my ($user, @args) = @_;
898 my $cmd = shift @args;
900 if ($cmd =~ /(not?id|noidentify)/) {
904 if (defined($args[0]) and $args[0] =~ /(not?id|noidentify)/) {
909 if(!$cmd or $cmd =~ /^list$/i) {
911 foreach my $tuser (chanserv
::get_users_nochans
($noid)) {
912 push @reply, get_user_nick
($tuser);
914 notice
($user, "Users in zero channels", @reply);
916 elsif($cmd =~ /^uinfo$/i) {
917 notice
($user, get_uinfo
($user, chanserv
::get_users_nochans
($noid)));
919 elsif($cmd =~ /^kill$/i) {
920 unless(adminserv
::can_do
($user, 'KILL')) {
921 notice
($user, $err_deny);
924 foreach my $tuser (chanserv
::get_users_nochans
($noid)) {
925 $tuser->{AGENT
} = $osnick;
926 nickserv
::kill_user
($tuser,
927 "Killed by \002".get_user_nick
($user)."\002".
928 (@args ? ": ".join(' ', @args) : '')
932 elsif($cmd =~ /^kline$/i) {
933 unless(adminserv
::is_svsop
($user, adminserv
::S_OPER
())) {
934 notice
($user, $err_deny);
937 foreach my $tuser (chanserv
::get_users_nochans
($noid)) {
938 $tuser->{AGENT
} = $osnick;
939 nickserv
::kline_user
($tuser, services_conf_chankilltime
,
940 "K:Lined by \002".get_user_nick
($user)."\002".
941 (@args ? ": ".join(' ', @args) : '')
945 elsif($cmd =~ /^(msg|message|notice)$/i) {
946 notice
($user, "Must have message to send") unless(@args);
947 foreach my $tuser (chanserv
::get_users_nochans
($noid)) {
948 $tuser->{AGENT
} = $osnick;
950 "Automated message from \002".get_user_nick
($user),
955 elsif($cmd =~ /^fjoin$/i) {
956 unless(adminserv
::can_do
($user, 'FJOIN')) {
957 notice
($user, $err_deny);
961 if ($args[0] !~ /^#/) {
962 notice
($user, "\002".$args[0]."\002 is not a valid channel name");
966 foreach my $tuser (chanserv
::get_users_nochans
($noid)) {
967 $tuser->{AGENT
} = $osnick;
968 ircd
::svsjoin
($osnick, get_user_nick
($tuser), $args[0]);
972 notice
($user, "Unknown LONERS command: $cmd",
973 'Syntax: OS LONERS [LIST|UINFO|MSG|FJOIN|KILL|KLINE] [NOID] [msg/reason]');
977 sub os_svskill
($$$) {
978 my ($user, $targets, $reason) = @_;
981 if(!adminserv
::is_svsop
($user, adminserv
::S_ROOT
())) {
982 notice
($user, $err_deny);
983 return $event::SUCCESS
;
986 foreach my $target (split(',', $targets)) {
987 #my $tuser = { NICK => $target };
988 if (!is_online
({ NICK
=> $target })) {
989 notice
($user, $target.' is not online.');
990 return $event::SUCCESS
;
993 ircd
::svskill
($osnick, $target, $reason);
996 return $event::SUCCESS
;
1000 my ($user, $targets, $reason) = @_;
1003 if(!adminserv
::can_do
($user, 'KILL')) {
1004 notice
($user, $err_deny);
1005 return $event::SUCCESS
;
1008 foreach my $target (split(',', $targets)) {
1009 my $tuser = { NICK
=> $target, AGENT
=> $osnick };
1010 if (!get_user_id
($tuser)) {
1011 notice
($user, $target.' is not online.');
1012 return $event::SUCCESS
;
1015 nickserv
::kill_user
($tuser, "Killed by ".get_user_nick
($user).($reason ? ': '.$reason : ''));
1020 sub os_gline
($$$@) {
1021 my ($user, $zline, $target, @args) = @_;
1024 return unless ($opernick = adminserv
::is_svsop
($user, adminserv
::S_OPER
));
1026 my $expiry = parse_time
(shift @args) if $args[0] =~ /^\+/;
1027 my $reason = join(' ', @args);
1028 $reason =~ s/^\:// if $reason;
1030 if($target =~ /^-/) {
1036 if($target =~ /\!/) {
1037 notice
($user, "Invalid G:line target \002$target\002");
1040 elsif($target =~ /^(\S+)\@(\S+)$/) {
1041 ($ident, $host) = ($1, $2);
1042 } elsif($target =~ /\./) {
1043 ($ident, $host) = ('*', $target);
1044 } elsif(valid_nick
($target)) {
1045 my $tuser = { NICK
=> $target };
1046 unless(get_user_id
($tuser)) {
1047 notice
($user, "Unknown user \002$target\002");
1051 (undef, $host) = nickserv
::get_host
($tuser);
1054 $host = nickserv
::get_ip
($tuser);
1057 notice
($user, "Invalid G:line target \002$target\002");
1062 ircd
::kline
($opernick, $ident, $host, $expiry, $reason);
1064 ircd
::unkline
($opernick, $ident, $host);
1068 if($ident and $ident !~ /^\**$/) {
1069 notice
($user, "You cannot specify an ident in a Z:line");
1071 elsif ($host =~ /^(?:\d{1,3}\.){3}(?:\d{1,3})/) {
1072 # all is well, do nothing
1074 elsif ($host =~ /^[0-9\/\
*\?\
.]+$/) {
1075 # This may allow invalid CIDR, not sure.
1076 # We're trusting our opers to not do stupid things.
1077 # THIS MAY BE A SOURCE OF BUGS.
1079 # all is well, do nothing
1081 notice
($user, "Z:lines can only be placed on IPs or IP ranges");
1085 ircd
::zline
($opernick, $host, $expiry, $reason);
1087 ircd
::unzline
($opernick, $host);
1091 return $event::SUCCESS
;
1095 my ($user, @args) = @_;
1096 my $cmd = shift @args;
1097 my $target = shift @args;
1099 if($cmd =~ /^list$/i) {
1101 foreach my $tuser (get_clones
($target)) {
1102 push @data, [get_user_nick
($tuser), (is_online
($tuser) ? "\002Online\002" : "\002Offline\002")];
1104 notice
($user, columnar
{TITLE
=> "Clones matching \002$target\002",
1105 NOHIGHLIGHT
=> nr_chk_flag_user
($user, NRF_NOHIGHLIGHT
)}, @data);
1107 elsif($cmd =~ /^uinfo$/i) {
1108 notice
($user, get_uinfo
($user, get_clones
($target)));
1110 elsif($cmd =~ /^kill$/i) {
1111 unless(adminserv
::can_do
($user, 'KILL')) {
1112 notice
($user, $err_deny);
1115 foreach my $tuser (get_clones
($target)) {
1116 next unless is_online
($tuser);
1117 $tuser->{AGENT
} = $osnick;
1118 nickserv
::kill_user
($tuser,
1119 "Killed by \002".get_user_nick
($user)."\002".
1120 (@args ? ": ".join(' ', @args) : '')
1124 elsif($cmd =~ /^kline$/i) {
1125 unless(adminserv
::is_svsop
($user, adminserv
::S_OPER
())) {
1126 notice
($user, $err_deny);
1129 foreach my $tuser (get_clones
($target)) {
1130 next unless is_online
($tuser);
1131 $tuser->{AGENT
} = $osnick;
1132 nickserv
::kline_user
($tuser, services_conf_chankilltime
,
1133 "K:Lined by \002".get_user_nick
($user)."\002".
1134 (@args ? ": ".join(' ', @args) : '')
1138 elsif($cmd =~ /^(msg|message|notice)$/i) {
1139 notice
($user, "Must have message to send") unless(@args);
1140 foreach my $tuser (get_clones
($target)) {
1141 next unless is_online
($tuser);
1142 $tuser->{AGENT
} = $osnick;
1144 "Automated message from \002".get_user_nick
($user),
1149 elsif($cmd =~ /^fjoin$/i) {
1150 unless(adminserv
::can_do
($user, 'FJOIN')) {
1151 notice
($user, $err_deny);
1155 if ($args[0] !~ /^#/) {
1156 notice
($user, "\002".$args[0]."\002 is not a valid channel name");
1160 foreach my $tuser (get_clones
($target)) {
1161 next unless is_online
($tuser);
1162 $tuser->{AGENT
} = $osnick;
1163 ircd
::svsjoin
($osnick, get_user_nick
($tuser), $args[0]);
1167 notice
($user, "Unknown CLONES command: $cmd",
1168 'Syntax: OS CLONES [LIST|UINFO|MSG|FJOIN|KILL|KLINE] [msg/reason]');
1175 my ($nick, $type) = @_;
1177 my ($banner, @reply);
1180 $banner = "\002Logon News\002";
1182 elsif ($type eq 'o') {
1183 $banner = "\002Oper News\002";
1185 $get_logonnews->execute($type);
1186 while(my ($adder, $time, $msg) = $get_logonnews->fetchrow_array) {
1187 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($time);
1189 push @reply, "[$banner ".$months[$mon]." $mday $year] $msg";
1191 $get_logonnews->finish();
1192 ircd
::notice
(main_conf_local
, $nick, @reply) if scalar(@reply);
1196 my ($user, $perm) = @_;
1198 if(adminserv
::can_do
($user, $perm)) {
1202 notice
($user, $err_deny);
1207 add_timer
('OperServ Expire', 60, __PACKAGE__
, 'operserv::expire');
1209 $get_expired_qlines->execute();
1210 while (my ($mask) = $get_expired_qlines->fetchrow_array() ) {
1211 ircd
::unsqline
($mask);
1212 $del_qline->execute($mask);
1214 $get_expired_qlines->finish();
1216 #don't run this code yet.
1218 $get_expired_akills->execute();
1219 while (my ($mask) = $get_expired_akills->fetchrow_array() ) {
1220 ($ident, $host) = split('@', $mask);
1221 ircd
::unkline
($osnick, $ident, $host);
1222 $del_akill->execute($mask);
1224 $get_expired_akills->finish();
1227 $del_expired_logonnews->execute();
1231 my ($user, @userlist) = @_;
1233 foreach my $tuser (@userlist) {
1234 my ($ident, $host, $vhost, $gecos, $server) = get_user_info
($tuser);
1235 my $modes = nickserv
::get_user_modes
($tuser);
1236 my $target = get_user_nick
($tuser);
1238 my ($curchans, $oldchans) = chanserv
::get_user_chans_recent
($tuser);
1241 ["Status:", (nickserv
::is_online
($tuser) ? "Online" : "Offline")],
1242 ["ID Nicks:", join(', ', nickserv
::get_id_nicks
($tuser))],
1243 ["Channels:", join(', ', @$curchans)],
1244 ["Recently Parted:", join(', ', @$oldchans)],
1245 ["Flood level:", get_flood_level
($tuser)],
1246 ["Hostmask:", "$target\!$ident\@$vhost"],
1248 ["Connecting from:", "$host"],
1249 ["Current Server:", $server],
1252 if(module
::is_loaded
('country')) {
1253 push @data, ["Country:", country
::get_user_country_long
($tuser)];
1254 } elsif(module
::is_loaded
('geoip')) {
1255 push @data, ["Location:", geoip
::stringify_location
(geoip
::get_user_location
($tuser))];
1258 push @reply, columnar
{TITLE
=> "User info for \002$target\002:",
1259 NOHIGHLIGHT
=> nr_chk_flag_user
($user, NRF_NOHIGHLIGHT
)}, @data;
1267 foreach my $target (split(',', $targets)) {
1268 my $sth; # statement handle. You'll see what I'll do with it next!
1269 if($target =~ /^(?:\d{1,3}\.){3}\d{1,3}$/) {
1270 $sth = $get_clones_fromipv4;
1271 } elsif($target =~ /\./) { # doesn't really work with localhost. oh well.
1272 $sth = $get_clones_fromhost;
1274 $sth = $get_clones_fromnick;
1277 $sth->execute($target);
1278 while(my ($nick, $id, $online) = $sth->fetchrow_array()) {
1279 push @users, { NICK
=> $nick, ID
=> $id, ONLINE
=> $online };