]> jfr.im git - irc/SurrealServices/srsv.git/blame - branches/0.4.3/modules/serviceslibs/adminserv.pm
Tell the user if they try to add a services oper that's not a registered nick.
[irc/SurrealServices/srsv.git] / branches / 0.4.3 / modules / serviceslibs / adminserv.pm
CommitLineData
7b261bb8 1# This file is part of SurrealServices.
2#
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.
7#
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.
12#
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
16package adminserv;
17
18use strict;
19
20use SrSv::Agent;
21
22use SrSv::Text::Format qw(columnar);
23use SrSv::Errors;
24
25use SrSv::User qw(get_user_nick get_user_id);
26use SrSv::User::Notice;
27use SrSv::Help qw( sendhelp );
28
29use SrSv::Log;
30
31use SrSv::NickReg::Flags qw(NRF_NOHIGHLIGHT nr_chk_flag_user);
32
33use SrSv::MySQL '$dbh';
34
35use constant {
36 S_HELP => 1,
37 S_OPER => 2,
38 S_ADMIN => 3,
39 S_ROOT => 4,
40};
41
d851df6a 42our (%flags, @levels, @defflags, $allflags);
43
44BEGIN {
6596c525 45# BE CAREFUL CHANGING THESE
7b261bb8 46my @flags = (
912ac8a0 47 'LOG',
7b261bb8 48 'SERVOP',
49 'FJOIN',
50 'SUPER',
51 'HOLD',
52 'FREEZE',
53 'BOT',
54 'QLINE',
55 'KILL',
56 'HELP',
57);
d851df6a 58
7696b939 59for(my $i = scalar(@flags) - 1; $i >= 0; $i--) {
6596c525 60 $flags{$flags[$i]} = 1 << $i;
7b261bb8 61}
6596c525 62$allflags = (1 << scalar(@flags)) - 1;
7b261bb8 63our @levels = ('Normal User', 'HelpOp', 'Operator', 'Administrator', 'Root');
6596c525 64# BE CAREFUL CHANGING THESE
d851df6a 65our @defflags = (
7b261bb8 66 0, # Unused
67 $flags{HELP}, # HelpOp
68 $flags{HELP}|$flags{FJOIN}|$flags{QLINE}|$flags{SUPER}|$flags{FREEZE}|$flags{KILL}, # Operator
d851df6a 69 $flags{HELP}|$flags{FJOIN}|$flags{QLINE}|$flags{SUPER}|$flags{FREEZE}|$flags{KILL}|
912ac8a0 70 $flags{HOLD}|$flags{BOT}|$flags{SERVOP}|$flags{LOG}, # Admin
7b261bb8 71 $allflags # Root
72);
73
d851df6a 74}
75our $asnick_default = 'AdminServ';
76our $asnick = $asnick_default;
77
78
7b261bb8 79our (
80 $create_svsop, $delete_svsop, $rename_svsop,
81
82 $get_svs_list, $get_all_svsops,
83
84 $get_svs_level, $set_svs_level, $get_best_svs_level,
85
86 $chk_pass, $get_pass, $set_pass
87);
88
89sub init() {
90 $create_svsop = $dbh->prepare("INSERT IGNORE INTO svsop SELECT id, NULL, NULL FROM nickreg WHERE nick=?");
91 $delete_svsop = $dbh->prepare("DELETE FROM svsop USING svsop, nickreg WHERE nickreg.nick=? AND svsop.nrid=nickreg.id");
92
93 $get_svs_list = $dbh->prepare("SELECT nickreg.nick, svsop.adder FROM svsop, nickreg WHERE svsop.level=? AND svsop.nrid=nickreg.id ORDER BY nickreg.nick");
94 $get_all_svsops = $dbh->prepare("SELECT nickreg.nick, svsop.level, svsop.adder FROM svsop, nickreg WHERE svsop.nrid=nickreg.id ORDER BY svsop.level, nickreg.nick");
95
96 $get_svs_level = $dbh->prepare("SELECT svsop.level FROM svsop, nickalias WHERE nickalias.alias=? AND svsop.nrid=nickalias.nrid");
97 $set_svs_level = $dbh->prepare("UPDATE svsop, nickreg SET svsop.level=?, svsop.adder=? WHERE nickreg.nick=? AND svsop.nrid=nickreg.id");
98 $get_best_svs_level = $dbh->prepare("SELECT svsop.level, nickreg.nick FROM nickid, nickreg, svsop WHERE nickid.nrid=nickreg.id AND svsop.nrid=nickreg.id AND nickid.id=? ORDER BY level DESC LIMIT 1");
99
100 $chk_pass = $dbh->prepare("SELECT 1 FROM ircop WHERE nick=? AND pass=?");
101 $get_pass = $dbh->prepare("SELECT pass FROM ircop WHERE nick=?");
102 $set_pass = $dbh->prepare("UPDATE ircop SET pass=? WHERE nick=?");
103}
104
105### ADMINSERV COMMANDS ###
106
107sub dispatch($$$) {
108 my ($src, $dst, $msg) = @_;
109 $msg =~ s/^\s+//;
110 my @args = split(/\s+/, $msg);
111 my $cmd = shift @args;
112
113 my $user = { NICK => $src, AGENT => $dst };
114
115 services::ulog($asnick, LOG_INFO(), "cmd: [$msg]", $user);
116
117 unless(is_svsop($user) or is_ircop($user)) {
118 notice($user, $err_deny);
119 ircd::globops($asnick, "\002$src\002 failed access to $asnick $msg");
120 return;
121 }
122
123 if($cmd =~ /^svsop$/i) {
124 my $cmd2 = shift @args;
125
126 if($cmd2 =~ /^add$/i) {
127 if(@args == 2 and $args[1] =~ /^[aoh]$/i) {
128 as_svs_add($user, $args[0], num_level($args[1]));
129 } else {
130 notice($user, 'Syntax: SVSOP ADD <nick> <A|O|H>');
131 }
132 }
133 elsif($cmd2 =~ /^del$/i) {
134 if(@args == 1) {
135 as_svs_del($user, $args[0]);
136 } else {
137 notice($user, 'Syntax: SVSOP DEL <nick>');
138 }
139 }
140 elsif($cmd2 =~ /^list$/i) {
141 if(@args == 1 and $args[0] =~ /^[raoh]$/i) {
142 as_svs_list($user, num_level($args[0]));
143 } else {
144 notice($user, 'Syntax: SVSOP LIST <R|A|O|H>');
145 }
146 }
147 else {
148 notice($user, 'Syntax: SVSOP <ADD|DEL|LIST> [...]');
149 }
150 }
151 elsif($cmd =~ /^whois$/i) {
152 if(@args == 1) {
153 as_whois($user, $args[0]);
154 } else {
155 notice($user, 'Syntax: WHOIS <nick>');
156 }
157 }
158 elsif($cmd =~ /^help$/i) {
159 sendhelp($user, 'adminserv', @args)
160 }
161 elsif($cmd =~ /^staff$/i) {
162 if(@args == 0) {
163 as_staff($user);
164 }
165 else {
166 notice($user, 'Syntax: STAFF');
167 }
168 }
169 else {
170 notice($user, "Unrecognized command. For help, type: \002/msg adminserv help\002");
171 }
172}
173
174sub as_svs_add($$$) {
674fc60a 175 my ($user, $target_nick, $target_level) = @_;
7b261bb8 176 my $src = get_user_nick($user);
674fc60a 177 my $target_nickreg = nickserv::get_root_nick($target_nick);
178 my ($src_level, $user_oper) = get_best_svs_level($user);
7b261bb8 179
674fc60a 180 if($target_level >= $src_level) {
7b261bb8 181 notice($user, $err_deny);
182 return;
183 }
184
68579027 185 if (!$target_nickreg) {
186 notice($user, "$target_nick is not registered.";
187 return;
188 }
189
674fc60a 190 $create_svsop->execute($target_nickreg);
191 $set_svs_level->execute($target_level, $user_oper, $target_nickreg);
192
193 notice($user, "\002$target_nick\002 is now a \002Services $levels[$target_level]\002.");
194 wlog($asnick, LOG_INFO(), "$src added $target_nickreg as a Services $levels[$target_level].");
7b261bb8 195}
196
197sub as_svs_del($$) {
674fc60a 198 my ($user, $target_nick) = @_;
7b261bb8 199 my $src = get_user_nick($user);
674fc60a 200 my $target_nickreg = nickserv::get_root_nick($target_nick);
7b261bb8 201
674fc60a 202 if(get_svs_level($target_nickreg) >= get_svs_level($src)) {
7b261bb8 203 notice($user, $err_deny);
204 return;
205 }
674fc60a 206
207 $delete_svsop->execute($target_nickreg);
208 notice($user, "\002$target_nickreg\002 has been stripped of services rank.");
209 wlog($asnick, LOG_INFO(), "$src stripped $target_nickreg of services rank.")
7b261bb8 210}
211
212sub as_svs_list($$) {
213 my ($user, $level) = @_;
214 my (@data, @reply);
215
216 $get_svs_list->execute($level);
217
218 while(my ($nick, $adder) = $get_svs_list->fetchrow_array) {
219 push @data, [$nick, "($adder)"];
220 }
221
222 notice($user, columnar({TITLE => "Services $levels[$level] list:",
223 NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT)}, @data));
224}
225
226sub as_whois($$) {
227 my ($user, $nick) = @_;
228
229 my ($level, $root) = get_best_svs_level({ NICK => $nick });
230 notice($user, "\002$nick\002 is a Services $levels[$level]".($level ? ' due to identification to the nick '."\002$root\002." : ''));
231}
232
233sub as_staff($) {
234 my ($user) = @_;
235 my (@data);
236
237 $get_all_svsops->execute();
238
239 while(my ($nick, $level, $adder) = $get_all_svsops->fetchrow_array) {
240 push @data, [$nick, $levels[$level], "($adder)"];
241 }
242
243 notice($user, columnar({TITLE => 'Staff list:',
244 NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT)}, @data));
245}
246
247
248### DATABASE UTILITY FUNCTIONS ###
249
250sub validate_chg($$) {
674fc60a 251 my ($user, $target_nick) = @_;
252 my ($user_oper);
7b261bb8 253
674fc60a 254 unless($user_oper = is_svsop($user, S_ROOT)) {
7b261bb8 255 notice($user, $err_deny);
256 return undef;
257 }
258
674fc60a 259 my $target_nickreg = nickserv::get_root_nick($target_nick);
260 unless($target_nickreg) {
261 notice($user, "The nick \002$target_nick\002 is not registered.");
7b261bb8 262 return undef;
263 }
264
674fc60a 265 return ($target_nickreg, $user_oper);
7b261bb8 266}
267
268sub can_do($$) {
269 my ($user, $flag) = @_;
270 my $nflag = $flags{$flag};
271
272 my ($level, $nick) = get_best_svs_level($user);
273
274 if($defflags[$level] & $nflag) {
275 return $nick if (($nflag == $flags{'HELP'}) or is_ircop($user));
276 }
277
278 return undef;
279}
280
281sub is_svsop($;$) {
282 my ($user, $rlev) = @_;
283
284 my ($level, $nick) = get_best_svs_level($user);
285 return $nick if(defined($level) and !defined($rlev));
286
287 if($level >= $rlev) {
288 return $nick if (($rlev == S_HELP) or is_ircop($user));
289 }
290
291 return undef;
292}
293
294sub is_ircop($) {
295 my ($user) = @_;
296
297 return undef if is_agent($user->{NICK});
298
299 return $user->{IRCOP} if(exists($user->{IRCOP}));
300
301 my %umodes = modes::splitumodes(nickserv::get_user_modes($user));
302
303 no warnings 'deprecated';
304 if(($umodes{'o'} eq '+') or ($umodes{'S'} eq '+')) {
305 $user->{IRCOP} = 1;
306 }
307 else {
308 $user->{IRCOP} = 0;
309 }
310
311 return $user->{IRCOP};
312}
313
314sub is_service($) {
315# detect if a user belongs to another service like NeoStats. only works if they set umode +S
316# is_ircop() includes is_service(), so no reason to call both.
317 my ($user) = @_;
318
319 return undef if is_agent($user->{NICK});
320
321 return $user->{SERVICE} if(exists($user->{SERVICE}));
322
323 my %umodes = modes::splitumodes(nickserv::get_user_modes($user));
324
325 if($umodes{'S'} eq '+') {
326 $user->{SERVICE} = 1;
327 $user->{IRCOP} = 1;
328 }
329 else {
330 $user->{SERVICE} = 0;
331 }
332
333 return $user->{SERVICE};
334}
335
336sub get_svs_level($) {
337 my ($nick) = @_;
338
339 return undef if is_agent($nick);
340
341 $get_svs_level->execute($nick);
342 my ($level) = $get_svs_level->fetchrow_array;
343
344 return $level or 0;
345}
346
347sub get_best_svs_level($) {
348 my ($user) = @_;
349
350 return undef if is_agent($user->{NICK});
351
352 if(exists($user->{SVSOP_LEVEL}) && exists($user->{SVSOP_NICK})) {
353 if(wantarray) {
354 return ($user->{SVSOP_LEVEL}, $user->{SVSOP_NICK});
355 } else {
356 return $user->{SVSOP_LEVEL};
357 }
358 }
359
360 my $uid = get_user_id($user);
361 $get_best_svs_level->execute($uid);
362 my ($level, $nick) = $get_best_svs_level->fetchrow_array;
363
364 $user->{SVSOP_LEVEL} = $level; $user->{SVSOP_NICK} = $nick;
365
366 if(wantarray) {
367 return ($level, $nick);
368 } else {
369 return $level;
370 }
371}
372
373### MISCELLANEA ###
374
375sub num_level($) {
376 my ($x) = @_;
377 $x =~ tr/hoarHOAR/12341234/;
378 return $x;
379}
380
381### IRC EVENTS ###
382
3831;