]> jfr.im git - irc/SurrealServices/srsv.git/blob - tags/0.4.3.1-pre2/modules/serviceslibs/adminserv.pm
cut of branches/0.4.3
[irc/SurrealServices/srsv.git] / tags / 0.4.3.1-pre2 / modules / serviceslibs / adminserv.pm
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
16 package adminserv;
17
18 use strict;
19
20 use SrSv::Agent;
21
22 use SrSv::Text::Format qw(columnar);
23 use SrSv::Errors;
24
25 use SrSv::User qw(get_user_nick get_user_id);
26 use SrSv::User::Notice;
27 use SrSv::Help qw( sendhelp );
28
29 use SrSv::Log;
30
31 use SrSv::NickReg::Flags qw(NRF_NOHIGHLIGHT nr_chk_flag_user);
32
33 use SrSv::MySQL '$dbh';
34
35 use constant {
36 S_HELP => 1,
37 S_OPER => 2,
38 S_ADMIN => 3,
39 S_ROOT => 4,
40 };
41
42 our (%flags, @levels, @defflags, $allflags);
43
44 BEGIN {
45 # BE CAREFUL CHANGING THESE
46 my @flags = (
47 'LOG',
48 'SERVOP',
49 'FJOIN',
50 'SUPER',
51 'HOLD',
52 'FREEZE',
53 'BOT',
54 'QLINE',
55 'KILL',
56 'HELP',
57 );
58
59 for(my $i = scalar(@flags) - 1; $i >= 0; $i--) {
60 $flags{$flags[$i]} = 1 << $i;
61 }
62 $allflags = (1 << scalar(@flags)) - 1;
63 our @levels = ('Normal User', 'HelpOp', 'Operator', 'Administrator', 'Root');
64 # BE CAREFUL CHANGING THESE
65 our @defflags = (
66 0, # Unused
67 $flags{HELP}, # HelpOp
68 $flags{HELP}|$flags{FJOIN}|$flags{QLINE}|$flags{SUPER}|$flags{FREEZE}|$flags{KILL}, # Operator
69 $flags{HELP}|$flags{FJOIN}|$flags{QLINE}|$flags{SUPER}|$flags{FREEZE}|$flags{KILL}|
70 $flags{HOLD}|$flags{BOT}|$flags{SERVOP}|$flags{LOG}, # Admin
71 $allflags # Root
72 );
73
74 }
75 our $asnick_default = 'AdminServ';
76 our $asnick = $asnick_default;
77
78
79 our (
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
89 sub 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
107 sub 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
174 sub as_svs_add($$$) {
175 my ($user, $target_nick, $target_level) = @_;
176 my $src = get_user_nick($user);
177 my $target_nickreg = nickserv::get_root_nick($target_nick);
178 my ($src_level, $user_oper) = get_best_svs_level($user);
179
180 if($target_level >= $src_level) {
181 notice($user, $err_deny);
182 return;
183 }
184
185 $create_svsop->execute($target_nickreg);
186 $set_svs_level->execute($target_level, $user_oper, $target_nickreg);
187
188 notice($user, "\002$target_nick\002 is now a \002Services $levels[$target_level]\002.");
189 wlog($asnick, LOG_INFO(), "$src added $target_nickreg as a Services $levels[$target_level].");
190 }
191
192 sub as_svs_del($$) {
193 my ($user, $target_nick) = @_;
194 my $src = get_user_nick($user);
195 my $target_nickreg = nickserv::get_root_nick($target_nick);
196
197 if(get_svs_level($target_nickreg) >= get_svs_level($src)) {
198 notice($user, $err_deny);
199 return;
200 }
201
202 $delete_svsop->execute($target_nickreg);
203 notice($user, "\002$target_nickreg\002 has been stripped of services rank.");
204 wlog($asnick, LOG_INFO(), "$src stripped $target_nickreg of services rank.")
205 }
206
207 sub as_svs_list($$) {
208 my ($user, $level) = @_;
209 my (@data, @reply);
210
211 $get_svs_list->execute($level);
212
213 while(my ($nick, $adder) = $get_svs_list->fetchrow_array) {
214 push @data, [$nick, "($adder)"];
215 }
216
217 notice($user, columnar({TITLE => "Services $levels[$level] list:",
218 NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT)}, @data));
219 }
220
221 sub as_whois($$) {
222 my ($user, $nick) = @_;
223
224 my ($level, $root) = get_best_svs_level({ NICK => $nick });
225 notice($user, "\002$nick\002 is a Services $levels[$level]".($level ? ' due to identification to the nick '."\002$root\002." : ''));
226 }
227
228 sub as_staff($) {
229 my ($user) = @_;
230 my (@data);
231
232 $get_all_svsops->execute();
233
234 while(my ($nick, $level, $adder) = $get_all_svsops->fetchrow_array) {
235 push @data, [$nick, $levels[$level], "($adder)"];
236 }
237
238 notice($user, columnar({TITLE => 'Staff list:',
239 NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT)}, @data));
240 }
241
242
243 ### DATABASE UTILITY FUNCTIONS ###
244
245 sub validate_chg($$) {
246 my ($user, $target_nick) = @_;
247 my ($user_oper);
248
249 unless($user_oper = is_svsop($user, S_ROOT)) {
250 notice($user, $err_deny);
251 return undef;
252 }
253
254 my $target_nickreg = nickserv::get_root_nick($target_nick);
255 unless($target_nickreg) {
256 notice($user, "The nick \002$target_nick\002 is not registered.");
257 return undef;
258 }
259
260 return ($target_nickreg, $user_oper);
261 }
262
263 sub can_do($$) {
264 my ($user, $flag) = @_;
265 my $nflag = $flags{$flag};
266
267 my ($level, $nick) = get_best_svs_level($user);
268
269 if($defflags[$level] & $nflag) {
270 return $nick if (($nflag == $flags{'HELP'}) or is_ircop($user));
271 }
272
273 return undef;
274 }
275
276 sub is_svsop($;$) {
277 my ($user, $rlev) = @_;
278
279 my ($level, $nick) = get_best_svs_level($user);
280 return $nick if(defined($level) and !defined($rlev));
281
282 if($level >= $rlev) {
283 return $nick if (($rlev == S_HELP) or is_ircop($user));
284 }
285
286 return undef;
287 }
288
289 sub is_ircop($) {
290 my ($user) = @_;
291
292 return undef if is_agent($user->{NICK});
293
294 return $user->{IRCOP} if(exists($user->{IRCOP}));
295
296 my %umodes = modes::splitumodes(nickserv::get_user_modes($user));
297
298 no warnings 'deprecated';
299 if(($umodes{'o'} eq '+') or ($umodes{'S'} eq '+')) {
300 $user->{IRCOP} = 1;
301 }
302 else {
303 $user->{IRCOP} = 0;
304 }
305
306 return $user->{IRCOP};
307 }
308
309 sub is_service($) {
310 # detect if a user belongs to another service like NeoStats. only works if they set umode +S
311 # is_ircop() includes is_service(), so no reason to call both.
312 my ($user) = @_;
313
314 return undef if is_agent($user->{NICK});
315
316 return $user->{SERVICE} if(exists($user->{SERVICE}));
317
318 my %umodes = modes::splitumodes(nickserv::get_user_modes($user));
319
320 if($umodes{'S'} eq '+') {
321 $user->{SERVICE} = 1;
322 $user->{IRCOP} = 1;
323 }
324 else {
325 $user->{SERVICE} = 0;
326 }
327
328 return $user->{SERVICE};
329 }
330
331 sub get_svs_level($) {
332 my ($nick) = @_;
333
334 return undef if is_agent($nick);
335
336 $get_svs_level->execute($nick);
337 my ($level) = $get_svs_level->fetchrow_array;
338
339 return $level or 0;
340 }
341
342 sub get_best_svs_level($) {
343 my ($user) = @_;
344
345 return undef if is_agent($user->{NICK});
346
347 if(exists($user->{SVSOP_LEVEL}) && exists($user->{SVSOP_NICK})) {
348 if(wantarray) {
349 return ($user->{SVSOP_LEVEL}, $user->{SVSOP_NICK});
350 } else {
351 return $user->{SVSOP_LEVEL};
352 }
353 }
354
355 my $uid = get_user_id($user);
356 $get_best_svs_level->execute($uid);
357 my ($level, $nick) = $get_best_svs_level->fetchrow_array;
358
359 $user->{SVSOP_LEVEL} = $level; $user->{SVSOP_NICK} = $nick;
360
361 if(wantarray) {
362 return ($level, $nick);
363 } else {
364 return $level;
365 }
366 }
367
368 ### MISCELLANEA ###
369
370 sub num_level($) {
371 my ($x) = @_;
372 $x =~ tr/hoarHOAR/12341234/;
373 return $x;
374 }
375
376 ### IRC EVENTS ###
377
378 1;